diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 4246d468..00000000 Binary files a/.DS_Store and /dev/null differ diff --git a/README.md b/README.md index 33d38a6f..a2443d26 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ # TOASTER-SERVER -![Frame 31](https://github.com/Link-MIND/TOASTER-Server/assets/92644651/c88432e0-5cb3-423f-9986-137fa4dfd5e1) +![image](https://github.com/user-attachments/assets/627ffad2-6206-4c96-bfce-7ae7574b3237) + 더 이상 **링크**를 태우지 마세요. **토스트 먹듯이 간단하게!** @@ -33,7 +34,8 @@ ## 📦 ERD -스크린샷 2024-01-19 오후 10 31 57 +![image](https://github.com/user-attachments/assets/46eed52a-e4bb-4001-9d6d-20246ca4ad1b) +

diff --git a/linkmind/build.gradle b/linkmind/build.gradle index d1526bf8..99c38747 100644 --- a/linkmind/build.gradle +++ b/linkmind/build.gradle @@ -34,28 +34,36 @@ dependencies { implementation 'mysql:mysql-connector-java:8.0.32' compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' + + runtimeOnly 'com.h2database:h2' + testImplementation 'org.springframework.boot:spring-boot-starter-test' - runtimeOnly 'com.mysql:mysql-connector-j' - implementation 'org.springframework.boot:spring-boot-starter-validation' + testImplementation group: 'com.h2database', name: 'h2', version: '2.2.224' + testCompileOnly 'org.projectlombok:lombok' + testAnnotationProcessor 'org.projectlombok:lombok' - implementation 'io.sentry:sentry-spring-boot-starter:5.7.0' - //JWT - implementation group: 'io.jsonwebtoken', name: 'jjwt-api', version: '0.11.2' - implementation group: 'io.jsonwebtoken', name: 'jjwt-impl', version: '0.11.2' - implementation group: 'io.jsonwebtoken', name: 'jjwt-jackson', version: '0.11.2' + runtimeOnly 'com.mysql:mysql-connector-j' + implementation 'org.springframework.boot:spring-boot-starter-validation' + + implementation 'io.sentry:sentry-spring-boot-starter:5.7.0' + //JWT + implementation group: 'io.jsonwebtoken', name: 'jjwt-api', version: '0.11.2' + implementation group: 'io.jsonwebtoken', name: 'jjwt-impl', version: '0.11.2' + implementation group: 'io.jsonwebtoken', name: 'jjwt-jackson', version: '0.11.2' // FCM implementation 'com.google.firebase:firebase-admin:9.1.1' implementation 'com.squareup.okhttp3:okhttp:4.10.0' // Firebase 서버로 푸시 메시지 전송 시 필요 - implementation 'org.springframework.boot:spring-boot-starter-actuator' - //Query DSL - implementation "com.querydsl:querydsl-jpa:${queryDslVersion}:jakarta" - annotationProcessor "com.querydsl:querydsl-apt:${queryDslVersion}:jakarta" - annotationProcessor "jakarta.annotation:jakarta.annotation-api" - annotationProcessor "jakarta.persistence:jakarta.persistence-api" + implementation 'org.springframework.boot:spring-boot-starter-actuator' + +//Query DSL + implementation "com.querydsl:querydsl-jpa:${queryDslVersion}:jakarta" + annotationProcessor "com.querydsl:querydsl-apt:${queryDslVersion}:jakarta" + annotationProcessor "jakarta.annotation:jakarta.annotation-api" + annotationProcessor "jakarta.persistence:jakarta.persistence-api" // S3 AWS implementation("software.amazon.awssdk:bom:2.21.0") @@ -71,9 +79,89 @@ dependencies { implementation 'io.sentry:sentry-spring-boot-starter:5.7.0' + // openfeign + implementation 'org.springframework.cloud:spring-cloud-starter-openfeign' + +} + +dependencyManagement { + imports { + mavenBom("org.springframework.cloud:spring-cloud-dependencies:2023.0.3") + } } +//sourceSets { +// main { +// java { +// srcDirs = ["$projectDir/src/main/java", "$projectDir/build/generated"] +// } +// } +//} +//compileJava { options.compilerArgs << '-parameters'} tasks.named('test') { - useJUnitPlatform() + useJUnitPlatform() +// finalizedBy 'jacocoTestReport' // test가 끝나면 jacocoTestReport 동작 } +//// jacoco report 설정 +//jacocoTestReport { +// reports { +// // html로 report 생성하기 +// // 빌드경로/jacoco/report.html 폴더 내부로 경로 설정 +// html.destination file("$jacoco/report.html") +// } +// +// // jacocoTestReport가 끝나면 jacocoTestCoverageVerification 동작 +// finalizedBy 'jacocoTestCoverageVerification' +//} +// +//// jacoco 커버리지 검증 설정 +//jacocoTestCoverageVerification { +// violationRules { +// rule { +// enabled = true // 커버리지 적용 여부 +// element = 'CLASS' // 커버리지 적용 단위 +// +// // 라인 커버리지 설정 +// // 적용 대상 전체 소스 코드들을 한줄 한줄 따졌을 때 테스트 코드가 작성되어 있는 줄의 빈도 +// // 테스트 코드가 작성되어 있는 비율이 90% 이상이어야 함 +// limit { +// counter = 'LINE' +// value = 'COVEREDRATIO' +// minimum = 0.10 +// } +// +// // 브랜치 커버리지 설정 +// // if-else 등을 활용하여 발생되는 분기들 중 테스트 코드가 작성되어 있는 빈도 +// // 테스트 코드가 작성되어 있는 비율이 90% 이상이어야 함 +// limit { +// counter = 'BRANCH' +// value = 'COVEREDRATIO' +// minimum = 0.00 +// } +// +// // 라인 최대 갯수 설정 +// // 빈 줄을 제외하고 하나의 자바 파일에서 작성될 수 있는 최대 라인 갯수 +// // 한 파일에 최대 500줄까지 작성되어야 함 +// limit { +// counter = 'LINE' +// value = 'TOTALCOUNT' +// maximum = 500 +// } +// // 커버리지 체크를 제외할 클래스들 +// excludes = [ +// // '*.test.*', +// '*.common.*', +// '*.config.*', +// '*.controller.*', +// '*.domain.*', +// '*.exception.*', +// '*.external.*', +// '*.infrastructure.*', +// '*.auth.*', +// '*.service.*' +// +// ] +// } +// } +//} diff --git a/linkmind/src/main/java/com/app/toaster/ToasterApplication.java b/linkmind/src/main/java/com/app/toaster/ToasterApplication.java index cc0c595a..d85e4b44 100644 --- a/linkmind/src/main/java/com/app/toaster/ToasterApplication.java +++ b/linkmind/src/main/java/com/app/toaster/ToasterApplication.java @@ -3,6 +3,7 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cglib.core.Local; +import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.data.jpa.repository.config.EnableJpaAuditing; import javax.annotation.PostConstruct; @@ -10,6 +11,7 @@ import java.util.TimeZone; @SpringBootApplication +@EnableFeignClients @EnableJpaAuditing public class ToasterApplication { diff --git a/linkmind/src/main/java/com/app/toaster/controller/AuthController.java b/linkmind/src/main/java/com/app/toaster/auth/controller/AuthController.java similarity index 84% rename from linkmind/src/main/java/com/app/toaster/controller/AuthController.java rename to linkmind/src/main/java/com/app/toaster/auth/controller/AuthController.java index 0bd3f6a7..9d53f49c 100644 --- a/linkmind/src/main/java/com/app/toaster/controller/AuthController.java +++ b/linkmind/src/main/java/com/app/toaster/auth/controller/AuthController.java @@ -1,4 +1,4 @@ -package com.app.toaster.controller; +package com.app.toaster.auth.controller; import java.io.IOException; @@ -13,13 +13,13 @@ import com.app.toaster.common.dto.ApiResponse; // import com.app.toaster.config.UserId; -import com.app.toaster.config.UserId; -import com.app.toaster.controller.request.auth.SignInRequestDto; -import com.app.toaster.controller.response.auth.SignInResponseDto; -import com.app.toaster.controller.response.auth.TokenHealthDto; -import com.app.toaster.controller.response.auth.TokenResponseDto; +import com.app.toaster.common.config.UserId; +import com.app.toaster.auth.controller.request.SignInRequestDto; +import com.app.toaster.auth.controller.response.SignInResponseDto; +import com.app.toaster.auth.controller.response.TokenHealthDto; +import com.app.toaster.auth.controller.response.TokenResponseDto; import com.app.toaster.exception.Success; -import com.app.toaster.service.auth.AuthService; +import com.app.toaster.auth.service.AuthService; import lombok.RequiredArgsConstructor; diff --git a/linkmind/src/main/java/com/app/toaster/controller/request/auth/SignInRequestDto.java b/linkmind/src/main/java/com/app/toaster/auth/controller/request/SignInRequestDto.java similarity index 59% rename from linkmind/src/main/java/com/app/toaster/controller/request/auth/SignInRequestDto.java rename to linkmind/src/main/java/com/app/toaster/auth/controller/request/SignInRequestDto.java index a8876bfe..72742468 100644 --- a/linkmind/src/main/java/com/app/toaster/controller/request/auth/SignInRequestDto.java +++ b/linkmind/src/main/java/com/app/toaster/auth/controller/request/SignInRequestDto.java @@ -1,4 +1,4 @@ -package com.app.toaster.controller.request.auth; +package com.app.toaster.auth.controller.request; public record SignInRequestDto(String socialType, String fcmToken) { } diff --git a/linkmind/src/main/java/com/app/toaster/controller/response/auth/SignInResponseDto.java b/linkmind/src/main/java/com/app/toaster/auth/controller/response/SignInResponseDto.java similarity index 89% rename from linkmind/src/main/java/com/app/toaster/controller/response/auth/SignInResponseDto.java rename to linkmind/src/main/java/com/app/toaster/auth/controller/response/SignInResponseDto.java index eee40bf6..a7269605 100644 --- a/linkmind/src/main/java/com/app/toaster/controller/response/auth/SignInResponseDto.java +++ b/linkmind/src/main/java/com/app/toaster/auth/controller/response/SignInResponseDto.java @@ -1,4 +1,4 @@ -package com.app.toaster.controller.response.auth; +package com.app.toaster.auth.controller.response; public record SignInResponseDto(Long userId, String accessToken, String refreshToken, String fcmToken, Boolean isRegistered,Boolean fcmIsAllowed, String profile) { public static SignInResponseDto of(Long userId, String accessToken, String refreshToken, String fcmToken, diff --git a/linkmind/src/main/java/com/app/toaster/controller/response/auth/TokenHealthDto.java b/linkmind/src/main/java/com/app/toaster/auth/controller/response/TokenHealthDto.java similarity index 75% rename from linkmind/src/main/java/com/app/toaster/controller/response/auth/TokenHealthDto.java rename to linkmind/src/main/java/com/app/toaster/auth/controller/response/TokenHealthDto.java index 7d65e852..42455b87 100644 --- a/linkmind/src/main/java/com/app/toaster/controller/response/auth/TokenHealthDto.java +++ b/linkmind/src/main/java/com/app/toaster/auth/controller/response/TokenHealthDto.java @@ -1,4 +1,4 @@ -package com.app.toaster.controller.response.auth; +package com.app.toaster.auth.controller.response; public record TokenHealthDto(boolean tokenHealth) { public static TokenHealthDto of(boolean tokenHealth){return new TokenHealthDto(tokenHealth);} diff --git a/linkmind/src/main/java/com/app/toaster/controller/response/auth/TokenResponseDto.java b/linkmind/src/main/java/com/app/toaster/auth/controller/response/TokenResponseDto.java similarity index 81% rename from linkmind/src/main/java/com/app/toaster/controller/response/auth/TokenResponseDto.java rename to linkmind/src/main/java/com/app/toaster/auth/controller/response/TokenResponseDto.java index 37f2e926..a162c1ec 100644 --- a/linkmind/src/main/java/com/app/toaster/controller/response/auth/TokenResponseDto.java +++ b/linkmind/src/main/java/com/app/toaster/auth/controller/response/TokenResponseDto.java @@ -1,4 +1,4 @@ -package com.app.toaster.controller.response.auth; +package com.app.toaster.auth.controller.response; public record TokenResponseDto(String accessToken, String refreshToken) { public static TokenResponseDto of(String accessToken, String refreshToken){ diff --git a/linkmind/src/main/java/com/app/toaster/service/auth/AuthService.java b/linkmind/src/main/java/com/app/toaster/auth/service/AuthService.java similarity index 79% rename from linkmind/src/main/java/com/app/toaster/service/auth/AuthService.java rename to linkmind/src/main/java/com/app/toaster/auth/service/AuthService.java index 60db12f2..497234f8 100644 --- a/linkmind/src/main/java/com/app/toaster/service/auth/AuthService.java +++ b/linkmind/src/main/java/com/app/toaster/auth/service/AuthService.java @@ -1,38 +1,41 @@ -package com.app.toaster.service.auth; +package com.app.toaster.auth.service; import java.io.IOException; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.client.RestTemplate; - -import com.app.toaster.common.dto.ApiResponse; -import com.app.toaster.config.jwt.JwtService; -import com.app.toaster.controller.request.auth.SignInRequestDto; -import com.app.toaster.controller.response.auth.SignInResponseDto; -import com.app.toaster.controller.response.auth.TokenHealthDto; -import com.app.toaster.controller.response.auth.TokenResponseDto; -import com.app.toaster.domain.SocialType; -import com.app.toaster.domain.User; + + +import com.app.toaster.common.config.jwt.JwtService; +import com.app.toaster.auth.controller.request.SignInRequestDto; +import com.app.toaster.auth.controller.response.SignInResponseDto; +import com.app.toaster.auth.controller.response.TokenHealthDto; +import com.app.toaster.auth.controller.response.TokenResponseDto; +import com.app.toaster.user.domain.SocialType; +import com.app.toaster.user.domain.User; import com.app.toaster.exception.Error; -import com.app.toaster.exception.Success; -import com.app.toaster.exception.model.BadRequestException; import com.app.toaster.exception.model.CustomException; import com.app.toaster.exception.model.NotFoundException; import com.app.toaster.exception.model.UnprocessableEntityException; +import com.app.toaster.external.client.discord.DiscordMessageProvider; +import com.app.toaster.external.client.discord.NotificationDto; +import com.app.toaster.external.client.discord.NotificationType; import com.app.toaster.external.client.slack.SlackApi; -import com.app.toaster.infrastructure.CategoryRepository; -import com.app.toaster.infrastructure.TimerRepository; -import com.app.toaster.infrastructure.ToastRepository; -import com.app.toaster.infrastructure.UserRepository; -import com.app.toaster.service.auth.apple.AppleSignInService; -import com.app.toaster.service.auth.kakao.KakaoSignInService; -import com.app.toaster.service.auth.kakao.LoginResult; -import com.app.toaster.service.toast.ToastService; +import com.app.toaster.category.infrastructure.CategoryRepository; +import com.app.toaster.timer.infrastructure.TimerRepository; +import com.app.toaster.toast.infrastructure.ToastRepository; +import com.app.toaster.user.infrastructure.UserRepository; +import com.app.toaster.popup.infrastructure.PopupManagerRepository; +import com.app.toaster.auth.service.apple.AppleSignInService; +import com.app.toaster.auth.service.kakao.KakaoSignInService; +import com.app.toaster.auth.service.kakao.LoginResult; +import com.app.toaster.toast.service.ToastService; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +@Slf4j @Service @RequiredArgsConstructor public class AuthService { @@ -43,8 +46,10 @@ public class AuthService { private final UserRepository userRepository; private final CategoryRepository categoryRepository; + private final PopupManagerRepository popupManagerRepository; private final SlackApi slackApi; + private final DiscordMessageProvider discordMessageProvider; private final Long TOKEN_EXPIRATION_TIME_ACCESS = 7*24*60 * 60 * 1000L; //7일 @@ -73,7 +78,8 @@ public SignInResponseDto signIn(String socialAccessToken, SignInRequestDto reque .socialType(socialType).build(); newUser.updateFcmIsAllowed(true); //신규 유저면 true박고 userRepository.save(newUser); - slackApi.sendSuccess(Success.LOGIN_SUCCESS); + + discordMessageProvider.sendNotification(new NotificationDto(NotificationType.SIGNUP,null,null)); } User user = userRepository.findBySocialIdAndSocialType(socialId, socialType) @@ -149,6 +155,7 @@ public void withdraw(Long userId) { } timerRepository.deleteAllByUser(user); categoryRepository.deleteAllByUser(user); + popupManagerRepository.deleteAllByUserId(userId); Long res = userRepository.deleteByUserId(userId); //res가 삭제된 컬럼의 개수 즉, 1이 아니면 뭔가 알 수 없는 에러. @@ -163,4 +170,5 @@ public TokenHealthDto checkHealthOfToken(String refreshToken){ return TokenHealthDto.of(jwtService.verifyToken(refreshToken)); } + } diff --git a/linkmind/src/main/java/com/app/toaster/service/auth/apple/AppleSignInService.java b/linkmind/src/main/java/com/app/toaster/auth/service/apple/AppleSignInService.java similarity index 78% rename from linkmind/src/main/java/com/app/toaster/service/auth/apple/AppleSignInService.java rename to linkmind/src/main/java/com/app/toaster/auth/service/apple/AppleSignInService.java index 7c7b8330..07630819 100644 --- a/linkmind/src/main/java/com/app/toaster/service/auth/apple/AppleSignInService.java +++ b/linkmind/src/main/java/com/app/toaster/auth/service/apple/AppleSignInService.java @@ -1,4 +1,4 @@ -package com.app.toaster.service.auth.apple; +package com.app.toaster.auth.service.apple; import java.security.PublicKey; import java.util.Map; @@ -7,11 +7,10 @@ import org.springframework.stereotype.Service; import org.springframework.web.client.RestClient; -import com.app.toaster.service.auth.apple.response.ApplePublicKeys; -import com.app.toaster.service.auth.apple.verify.AppleJwtParser; -import com.app.toaster.service.auth.apple.verify.PublicKeyGenerator; -import com.app.toaster.service.auth.kakao.LoginResult; -import com.mysql.cj.log.Log; +import com.app.toaster.auth.service.apple.response.ApplePublicKeys; +import com.app.toaster.auth.service.apple.verify.AppleJwtParser; +import com.app.toaster.auth.service.apple.verify.PublicKeyGenerator; +import com.app.toaster.auth.service.kakao.LoginResult; import io.jsonwebtoken.Claims; import lombok.RequiredArgsConstructor; diff --git a/linkmind/src/main/java/com/app/toaster/service/auth/apple/response/ApplePublicKey.java b/linkmind/src/main/java/com/app/toaster/auth/service/apple/response/ApplePublicKey.java similarity index 67% rename from linkmind/src/main/java/com/app/toaster/service/auth/apple/response/ApplePublicKey.java rename to linkmind/src/main/java/com/app/toaster/auth/service/apple/response/ApplePublicKey.java index 808848bc..eb321486 100644 --- a/linkmind/src/main/java/com/app/toaster/service/auth/apple/response/ApplePublicKey.java +++ b/linkmind/src/main/java/com/app/toaster/auth/service/apple/response/ApplePublicKey.java @@ -1,4 +1,4 @@ -package com.app.toaster.service.auth.apple.response; +package com.app.toaster.auth.service.apple.response; public record ApplePublicKey( String kty, diff --git a/linkmind/src/main/java/com/app/toaster/service/auth/apple/response/ApplePublicKeys.java b/linkmind/src/main/java/com/app/toaster/auth/service/apple/response/ApplePublicKeys.java similarity index 84% rename from linkmind/src/main/java/com/app/toaster/service/auth/apple/response/ApplePublicKeys.java rename to linkmind/src/main/java/com/app/toaster/auth/service/apple/response/ApplePublicKeys.java index 09be00ee..f7f56a30 100644 --- a/linkmind/src/main/java/com/app/toaster/service/auth/apple/response/ApplePublicKeys.java +++ b/linkmind/src/main/java/com/app/toaster/auth/service/apple/response/ApplePublicKeys.java @@ -1,12 +1,10 @@ -package com.app.toaster.service.auth.apple.response; +package com.app.toaster.auth.service.apple.response; import java.util.List; import com.app.toaster.exception.Error; import com.app.toaster.exception.model.CustomException; -import lombok.extern.slf4j.Slf4j; - public record ApplePublicKeys(List keys) { public ApplePublicKey getMatchesKey(String alg, String kid) { diff --git a/linkmind/src/main/java/com/app/toaster/service/auth/apple/verify/AppleJwtParser.java b/linkmind/src/main/java/com/app/toaster/auth/service/apple/verify/AppleJwtParser.java similarity index 97% rename from linkmind/src/main/java/com/app/toaster/service/auth/apple/verify/AppleJwtParser.java rename to linkmind/src/main/java/com/app/toaster/auth/service/apple/verify/AppleJwtParser.java index 1786b1d9..59febec3 100644 --- a/linkmind/src/main/java/com/app/toaster/service/auth/apple/verify/AppleJwtParser.java +++ b/linkmind/src/main/java/com/app/toaster/auth/service/apple/verify/AppleJwtParser.java @@ -1,4 +1,4 @@ -package com.app.toaster.service.auth.apple.verify; +package com.app.toaster.auth.service.apple.verify; import java.security.PublicKey; import java.util.Base64; diff --git a/linkmind/src/main/java/com/app/toaster/service/auth/apple/verify/EncryptUtils.java b/linkmind/src/main/java/com/app/toaster/auth/service/apple/verify/EncryptUtils.java similarity index 93% rename from linkmind/src/main/java/com/app/toaster/service/auth/apple/verify/EncryptUtils.java rename to linkmind/src/main/java/com/app/toaster/auth/service/apple/verify/EncryptUtils.java index 0635998e..d1939c09 100644 --- a/linkmind/src/main/java/com/app/toaster/service/auth/apple/verify/EncryptUtils.java +++ b/linkmind/src/main/java/com/app/toaster/auth/service/apple/verify/EncryptUtils.java @@ -1,4 +1,4 @@ -package com.app.toaster.service.auth.apple.verify; +package com.app.toaster.auth.service.apple.verify; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; diff --git a/linkmind/src/main/java/com/app/toaster/service/auth/apple/verify/PublicKeyGenerator.java b/linkmind/src/main/java/com/app/toaster/auth/service/apple/verify/PublicKeyGenerator.java similarity index 88% rename from linkmind/src/main/java/com/app/toaster/service/auth/apple/verify/PublicKeyGenerator.java rename to linkmind/src/main/java/com/app/toaster/auth/service/apple/verify/PublicKeyGenerator.java index 11080824..6b272d5f 100644 --- a/linkmind/src/main/java/com/app/toaster/service/auth/apple/verify/PublicKeyGenerator.java +++ b/linkmind/src/main/java/com/app/toaster/auth/service/apple/verify/PublicKeyGenerator.java @@ -1,4 +1,4 @@ -package com.app.toaster.service.auth.apple.verify; +package com.app.toaster.auth.service.apple.verify; import java.math.BigInteger; import java.security.KeyFactory; @@ -13,10 +13,8 @@ import com.app.toaster.exception.Error; import com.app.toaster.exception.model.CustomException; -import com.app.toaster.service.auth.apple.response.ApplePublicKey; -import com.app.toaster.service.auth.apple.response.ApplePublicKeys; - -import lombok.extern.slf4j.Slf4j; +import com.app.toaster.auth.service.apple.response.ApplePublicKey; +import com.app.toaster.auth.service.apple.response.ApplePublicKeys; @Component public class PublicKeyGenerator { diff --git a/linkmind/src/main/java/com/app/toaster/service/auth/kakao/KakaoAccount.java b/linkmind/src/main/java/com/app/toaster/auth/service/kakao/KakaoAccount.java similarity index 56% rename from linkmind/src/main/java/com/app/toaster/service/auth/kakao/KakaoAccount.java rename to linkmind/src/main/java/com/app/toaster/auth/service/kakao/KakaoAccount.java index 131902fd..d6582e1e 100644 --- a/linkmind/src/main/java/com/app/toaster/service/auth/kakao/KakaoAccount.java +++ b/linkmind/src/main/java/com/app/toaster/auth/service/kakao/KakaoAccount.java @@ -1,4 +1,4 @@ -package com.app.toaster.service.auth.kakao; +package com.app.toaster.auth.service.kakao; public record KakaoAccount(KakaoUserProfile profile) { } diff --git a/linkmind/src/main/java/com/app/toaster/service/auth/kakao/KakaoSignInService.java b/linkmind/src/main/java/com/app/toaster/auth/service/kakao/KakaoSignInService.java similarity index 96% rename from linkmind/src/main/java/com/app/toaster/service/auth/kakao/KakaoSignInService.java rename to linkmind/src/main/java/com/app/toaster/auth/service/kakao/KakaoSignInService.java index 814a4ba3..c0e1bbc5 100644 --- a/linkmind/src/main/java/com/app/toaster/service/auth/kakao/KakaoSignInService.java +++ b/linkmind/src/main/java/com/app/toaster/auth/service/kakao/KakaoSignInService.java @@ -1,4 +1,4 @@ -package com.app.toaster.service.auth.kakao; +package com.app.toaster.auth.service.kakao; import java.util.HashMap; import java.util.Map; @@ -13,7 +13,6 @@ import org.springframework.web.client.RestTemplate; import com.app.toaster.exception.Error; -import com.app.toaster.exception.model.ForbiddenException; import com.app.toaster.exception.model.UnprocessableEntityException; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.gson.JsonArray; diff --git a/linkmind/src/main/java/com/app/toaster/service/auth/kakao/KakaoUserProfile.java b/linkmind/src/main/java/com/app/toaster/auth/service/kakao/KakaoUserProfile.java similarity index 63% rename from linkmind/src/main/java/com/app/toaster/service/auth/kakao/KakaoUserProfile.java rename to linkmind/src/main/java/com/app/toaster/auth/service/kakao/KakaoUserProfile.java index 9044293c..5667e160 100644 --- a/linkmind/src/main/java/com/app/toaster/service/auth/kakao/KakaoUserProfile.java +++ b/linkmind/src/main/java/com/app/toaster/auth/service/kakao/KakaoUserProfile.java @@ -1,4 +1,4 @@ -package com.app.toaster.service.auth.kakao; +package com.app.toaster.auth.service.kakao; public record KakaoUserProfile(String nickname, String profileImageUrl) { } diff --git a/linkmind/src/main/java/com/app/toaster/service/auth/kakao/KakaoUserResponse.java b/linkmind/src/main/java/com/app/toaster/auth/service/kakao/KakaoUserResponse.java similarity index 80% rename from linkmind/src/main/java/com/app/toaster/service/auth/kakao/KakaoUserResponse.java rename to linkmind/src/main/java/com/app/toaster/auth/service/kakao/KakaoUserResponse.java index 253ce5b9..c87a5883 100644 --- a/linkmind/src/main/java/com/app/toaster/service/auth/kakao/KakaoUserResponse.java +++ b/linkmind/src/main/java/com/app/toaster/auth/service/kakao/KakaoUserResponse.java @@ -1,4 +1,4 @@ -package com.app.toaster.service.auth.kakao; +package com.app.toaster.auth.service.kakao; public record KakaoUserResponse(KakaoAccount kakaoAccount) { public static KakaoUserResponse of(KakaoAccount kakaoAccount){ diff --git a/linkmind/src/main/java/com/app/toaster/service/auth/kakao/LoginResult.java b/linkmind/src/main/java/com/app/toaster/auth/service/kakao/LoginResult.java similarity index 81% rename from linkmind/src/main/java/com/app/toaster/service/auth/kakao/LoginResult.java rename to linkmind/src/main/java/com/app/toaster/auth/service/kakao/LoginResult.java index 556e48c8..0cf22581 100644 --- a/linkmind/src/main/java/com/app/toaster/service/auth/kakao/LoginResult.java +++ b/linkmind/src/main/java/com/app/toaster/auth/service/kakao/LoginResult.java @@ -1,4 +1,4 @@ -package com.app.toaster.service.auth.kakao; +package com.app.toaster.auth.service.kakao; public record LoginResult(String id, String profile, String nickname) { public static LoginResult of(String id, String profile,String nickname){ diff --git a/linkmind/src/main/java/com/app/toaster/service/auth/kakao/Strategy.java b/linkmind/src/main/java/com/app/toaster/auth/service/kakao/Strategy.java similarity index 51% rename from linkmind/src/main/java/com/app/toaster/service/auth/kakao/Strategy.java rename to linkmind/src/main/java/com/app/toaster/auth/service/kakao/Strategy.java index bfcdf390..1ebd0793 100644 --- a/linkmind/src/main/java/com/app/toaster/service/auth/kakao/Strategy.java +++ b/linkmind/src/main/java/com/app/toaster/auth/service/kakao/Strategy.java @@ -1,4 +1,4 @@ -package com.app.toaster.service.auth.kakao; +package com.app.toaster.auth.service.kakao; public enum Strategy { WITHDRAWAL, diff --git a/linkmind/src/main/java/com/app/toaster/controller/CategoryController.java b/linkmind/src/main/java/com/app/toaster/category/controller/CategoryController.java similarity index 82% rename from linkmind/src/main/java/com/app/toaster/controller/CategoryController.java rename to linkmind/src/main/java/com/app/toaster/category/controller/CategoryController.java index 99e4446b..dd4cb164 100644 --- a/linkmind/src/main/java/com/app/toaster/controller/CategoryController.java +++ b/linkmind/src/main/java/com/app/toaster/category/controller/CategoryController.java @@ -1,27 +1,26 @@ -package com.app.toaster.controller; +package com.app.toaster.category.controller; +import com.app.toaster.category.controller.request.ChangeCateoryPriorityDto; +import com.app.toaster.category.controller.request.ChangeCateoryTitleDto; +import com.app.toaster.category.controller.request.CreateCategoryDto; import com.app.toaster.common.dto.ApiResponse; -import com.app.toaster.config.UserId; -import com.app.toaster.controller.request.category.*; -import com.app.toaster.controller.response.category.CategoriesResponse; -import com.app.toaster.controller.response.toast.ToastFilter; -import com.app.toaster.controller.response.category.CategoryResponse; -import com.app.toaster.controller.response.category.GetCategoryResponseDto; -import com.app.toaster.controller.valid.Severity; -import com.app.toaster.controller.valid.TitleValid; +import com.app.toaster.common.config.UserId; +import com.app.toaster.category.controller.response.CategoriesResponse; +import com.app.toaster.toast.controller.response.ToastFilter; +import com.app.toaster.category.controller.response.GetCategoryResponseDto; +import com.app.toaster.utils.valid.Severity; +import com.app.toaster.utils.valid.TitleValid; import com.app.toaster.exception.Success; -import com.app.toaster.service.category.CategoryService; -import com.app.toaster.service.search.SearchService; +import com.app.toaster.category.service.CategoryService; +import com.app.toaster.search.service.SearchService; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; -import jakarta.validation.constraints.Pattern; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import java.util.ArrayList; -import java.util.List; @RestController @RequestMapping("/category") diff --git a/linkmind/src/main/java/com/app/toaster/controller/request/category/ChangeCateoryPriorityDto.java b/linkmind/src/main/java/com/app/toaster/category/controller/request/ChangeCateoryPriorityDto.java similarity index 76% rename from linkmind/src/main/java/com/app/toaster/controller/request/category/ChangeCateoryPriorityDto.java rename to linkmind/src/main/java/com/app/toaster/category/controller/request/ChangeCateoryPriorityDto.java index c3276d18..4337f794 100644 --- a/linkmind/src/main/java/com/app/toaster/controller/request/category/ChangeCateoryPriorityDto.java +++ b/linkmind/src/main/java/com/app/toaster/category/controller/request/ChangeCateoryPriorityDto.java @@ -1,4 +1,4 @@ -package com.app.toaster.controller.request.category; +package com.app.toaster.category.controller.request; import jakarta.validation.constraints.NotNull; diff --git a/linkmind/src/main/java/com/app/toaster/controller/request/category/ChangeCateoryTitleDto.java b/linkmind/src/main/java/com/app/toaster/category/controller/request/ChangeCateoryTitleDto.java similarity index 57% rename from linkmind/src/main/java/com/app/toaster/controller/request/category/ChangeCateoryTitleDto.java rename to linkmind/src/main/java/com/app/toaster/category/controller/request/ChangeCateoryTitleDto.java index c05e4c16..9a9f7b53 100644 --- a/linkmind/src/main/java/com/app/toaster/controller/request/category/ChangeCateoryTitleDto.java +++ b/linkmind/src/main/java/com/app/toaster/category/controller/request/ChangeCateoryTitleDto.java @@ -1,7 +1,7 @@ -package com.app.toaster.controller.request.category; +package com.app.toaster.category.controller.request; -import com.app.toaster.controller.valid.Severity; -import com.app.toaster.controller.valid.TitleValid; +import com.app.toaster.utils.valid.Severity; +import com.app.toaster.utils.valid.TitleValid; import jakarta.validation.constraints.NotNull; public record ChangeCateoryTitleDto( diff --git a/linkmind/src/main/java/com/app/toaster/controller/request/category/CreateCategoryDto.java b/linkmind/src/main/java/com/app/toaster/category/controller/request/CreateCategoryDto.java similarity index 50% rename from linkmind/src/main/java/com/app/toaster/controller/request/category/CreateCategoryDto.java rename to linkmind/src/main/java/com/app/toaster/category/controller/request/CreateCategoryDto.java index 39ba634b..64807482 100644 --- a/linkmind/src/main/java/com/app/toaster/controller/request/category/CreateCategoryDto.java +++ b/linkmind/src/main/java/com/app/toaster/category/controller/request/CreateCategoryDto.java @@ -1,7 +1,7 @@ -package com.app.toaster.controller.request.category; +package com.app.toaster.category.controller.request; -import com.app.toaster.controller.valid.Severity; -import com.app.toaster.controller.valid.TitleValid; +import com.app.toaster.utils.valid.Severity; +import com.app.toaster.utils.valid.TitleValid; import jakarta.validation.constraints.NotNull; public record CreateCategoryDto(@TitleValid(payload = Severity.Error.class) @NotNull String categoryTitle) { diff --git a/linkmind/src/main/java/com/app/toaster/controller/request/category/DeleteCategoryDto.java b/linkmind/src/main/java/com/app/toaster/category/controller/request/DeleteCategoryDto.java similarity index 75% rename from linkmind/src/main/java/com/app/toaster/controller/request/category/DeleteCategoryDto.java rename to linkmind/src/main/java/com/app/toaster/category/controller/request/DeleteCategoryDto.java index a2c705c4..f65e784b 100644 --- a/linkmind/src/main/java/com/app/toaster/controller/request/category/DeleteCategoryDto.java +++ b/linkmind/src/main/java/com/app/toaster/category/controller/request/DeleteCategoryDto.java @@ -1,4 +1,4 @@ -package com.app.toaster.controller.request.category; +package com.app.toaster.category.controller.request; import jakarta.validation.constraints.NotNull; diff --git a/linkmind/src/main/java/com/app/toaster/controller/response/category/CategoriesResponse.java b/linkmind/src/main/java/com/app/toaster/category/controller/response/CategoriesResponse.java similarity index 84% rename from linkmind/src/main/java/com/app/toaster/controller/response/category/CategoriesResponse.java rename to linkmind/src/main/java/com/app/toaster/category/controller/response/CategoriesResponse.java index 6c4c5d79..abb89d63 100644 --- a/linkmind/src/main/java/com/app/toaster/controller/response/category/CategoriesResponse.java +++ b/linkmind/src/main/java/com/app/toaster/category/controller/response/CategoriesResponse.java @@ -1,4 +1,4 @@ -package com.app.toaster.controller.response.category; +package com.app.toaster.category.controller.response; import java.util.List; diff --git a/linkmind/src/main/java/com/app/toaster/controller/response/category/CategoryResponse.java b/linkmind/src/main/java/com/app/toaster/category/controller/response/CategoryResponse.java similarity index 73% rename from linkmind/src/main/java/com/app/toaster/controller/response/category/CategoryResponse.java rename to linkmind/src/main/java/com/app/toaster/category/controller/response/CategoryResponse.java index 8abb5567..84ee62f4 100644 --- a/linkmind/src/main/java/com/app/toaster/controller/response/category/CategoryResponse.java +++ b/linkmind/src/main/java/com/app/toaster/category/controller/response/CategoryResponse.java @@ -1,4 +1,4 @@ -package com.app.toaster.controller.response.category; +package com.app.toaster.category.controller.response; import lombok.Builder; diff --git a/linkmind/src/main/java/com/app/toaster/controller/response/category/DuplicatedResponse.java b/linkmind/src/main/java/com/app/toaster/category/controller/response/DuplicatedResponse.java similarity index 75% rename from linkmind/src/main/java/com/app/toaster/controller/response/category/DuplicatedResponse.java rename to linkmind/src/main/java/com/app/toaster/category/controller/response/DuplicatedResponse.java index 8254fbde..606fd69c 100644 --- a/linkmind/src/main/java/com/app/toaster/controller/response/category/DuplicatedResponse.java +++ b/linkmind/src/main/java/com/app/toaster/category/controller/response/DuplicatedResponse.java @@ -1,4 +1,4 @@ -package com.app.toaster.controller.response.category; +package com.app.toaster.category.controller.response; public record DuplicatedResponse(Boolean isDupicated) { public static DuplicatedResponse of(Boolean isDupicated){ diff --git a/linkmind/src/main/java/com/app/toaster/category/controller/response/GetCategoryResponseDto.java b/linkmind/src/main/java/com/app/toaster/category/controller/response/GetCategoryResponseDto.java new file mode 100644 index 00000000..4424f4f4 --- /dev/null +++ b/linkmind/src/main/java/com/app/toaster/category/controller/response/GetCategoryResponseDto.java @@ -0,0 +1,14 @@ +package com.app.toaster.category.controller.response; + +import com.app.toaster.toast.controller.response.ToastDto; +import lombok.Builder; + +import java.util.List; + +@Builder +public record GetCategoryResponseDto( + int allToastNum, + List toastListDto +){ + +} diff --git a/linkmind/src/main/java/com/app/toaster/domain/Category.java b/linkmind/src/main/java/com/app/toaster/category/domain/Category.java similarity index 88% rename from linkmind/src/main/java/com/app/toaster/domain/Category.java rename to linkmind/src/main/java/com/app/toaster/category/domain/Category.java index 82003080..9b3c34c7 100644 --- a/linkmind/src/main/java/com/app/toaster/domain/Category.java +++ b/linkmind/src/main/java/com/app/toaster/category/domain/Category.java @@ -1,12 +1,11 @@ -package com.app.toaster.domain; +package com.app.toaster.category.domain; import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.List; +import com.app.toaster.timer.domain.Reminder; +import com.app.toaster.user.domain.User; import jakarta.persistence.*; -import jakarta.validation.constraints.Size; import lombok.AccessLevel; import lombok.Builder; import lombok.Getter; diff --git a/linkmind/src/main/java/com/app/toaster/infrastructure/CategoryRepository.java b/linkmind/src/main/java/com/app/toaster/category/infrastructure/CategoryRepository.java similarity index 90% rename from linkmind/src/main/java/com/app/toaster/infrastructure/CategoryRepository.java rename to linkmind/src/main/java/com/app/toaster/category/infrastructure/CategoryRepository.java index 0b29c63b..12b86c06 100644 --- a/linkmind/src/main/java/com/app/toaster/infrastructure/CategoryRepository.java +++ b/linkmind/src/main/java/com/app/toaster/category/infrastructure/CategoryRepository.java @@ -1,20 +1,17 @@ -package com.app.toaster.infrastructure; +package com.app.toaster.category.infrastructure; import java.util.ArrayList; import java.util.List; -import java.util.Optional; -import com.app.toaster.controller.request.category.ChangeCateoryTitleDto; -import com.app.toaster.domain.User; +import com.app.toaster.user.domain.User; import org.springframework.data.jpa.repository.JpaRepository; -import com.app.toaster.domain.Category; +import com.app.toaster.category.domain.Category; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; -import org.springframework.transaction.annotation.Transactional; public interface CategoryRepository extends JpaRepository { diff --git a/linkmind/src/main/java/com/app/toaster/service/category/CategoryService.java b/linkmind/src/main/java/com/app/toaster/category/service/CategoryService.java similarity index 87% rename from linkmind/src/main/java/com/app/toaster/service/category/CategoryService.java rename to linkmind/src/main/java/com/app/toaster/category/service/CategoryService.java index b1fd6de1..fd7ffbd2 100644 --- a/linkmind/src/main/java/com/app/toaster/service/category/CategoryService.java +++ b/linkmind/src/main/java/com/app/toaster/category/service/CategoryService.java @@ -1,24 +1,26 @@ -package com.app.toaster.service.category; - -import com.app.toaster.controller.request.category.*; -import com.app.toaster.controller.response.category.CategoriesResponse; -import com.app.toaster.controller.response.category.DuplicatedResponse; -import com.app.toaster.controller.response.toast.ToastDto; -import com.app.toaster.controller.response.toast.ToastFilter; -import com.app.toaster.controller.response.category.CategoryResponse; -import com.app.toaster.controller.response.category.GetCategoryResponseDto; -import com.app.toaster.domain.Category; -import com.app.toaster.domain.Reminder; -import com.app.toaster.domain.Toast; -import com.app.toaster.domain.User; +package com.app.toaster.category.service; + +import com.app.toaster.category.controller.request.ChangeCateoryPriorityDto; +import com.app.toaster.category.controller.request.ChangeCateoryTitleDto; +import com.app.toaster.category.controller.request.CreateCategoryDto; +import com.app.toaster.category.controller.response.CategoriesResponse; +import com.app.toaster.category.controller.response.DuplicatedResponse; +import com.app.toaster.toast.controller.response.ToastDto; +import com.app.toaster.toast.controller.response.ToastFilter; +import com.app.toaster.category.controller.response.CategoryResponse; +import com.app.toaster.category.controller.response.GetCategoryResponseDto; +import com.app.toaster.category.domain.Category; +import com.app.toaster.timer.domain.Reminder; +import com.app.toaster.toast.domain.Toast; +import com.app.toaster.user.domain.User; import com.app.toaster.exception.Error; import com.app.toaster.exception.model.BadRequestException; import com.app.toaster.exception.model.CustomException; import com.app.toaster.exception.model.NotFoundException; -import com.app.toaster.infrastructure.CategoryRepository; -import com.app.toaster.infrastructure.TimerRepository; -import com.app.toaster.infrastructure.ToastRepository; -import com.app.toaster.infrastructure.UserRepository; +import com.app.toaster.category.infrastructure.CategoryRepository; +import com.app.toaster.timer.infrastructure.TimerRepository; +import com.app.toaster.toast.infrastructure.ToastRepository; +import com.app.toaster.user.infrastructure.UserRepository; import lombok.RequiredArgsConstructor; import lombok.val; diff --git a/linkmind/src/main/java/com/app/toaster/common/advice/ControllerExceptionAdvice.java b/linkmind/src/main/java/com/app/toaster/common/advice/ControllerExceptionAdvice.java index 6be80bc7..dfc59494 100644 --- a/linkmind/src/main/java/com/app/toaster/common/advice/ControllerExceptionAdvice.java +++ b/linkmind/src/main/java/com/app/toaster/common/advice/ControllerExceptionAdvice.java @@ -20,11 +20,15 @@ import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.context.request.WebRequest; import org.springframework.web.method.annotation.HandlerMethodValidationException; import com.app.toaster.common.dto.ApiResponse; import com.app.toaster.exception.Error; import com.app.toaster.exception.model.CustomException; +import com.app.toaster.external.client.discord.DiscordMessageProvider; +import com.app.toaster.external.client.discord.NotificationDto; +import com.app.toaster.external.client.discord.NotificationType; import com.app.toaster.external.client.slack.SlackApi; import io.sentry.Sentry; @@ -33,18 +37,20 @@ import jakarta.validation.ConstraintViolationException; import lombok.NoArgsConstructor; import lombok.RequiredArgsConstructor; +import org.springframework.web.servlet.resource.NoResourceFoundException; @RestControllerAdvice @Component @RequiredArgsConstructor public class ControllerExceptionAdvice { private final SlackApi slackApi; + private final DiscordMessageProvider discordMessageProvider; /** * custom error */ @ExceptionHandler(CustomException.class) - protected ResponseEntity handleCustomException(CustomException e) { + protected ResponseEntity handleCustomException(CustomException e , WebRequest request) { Sentry.captureException(e); return ResponseEntity.status(e.getHttpStatus()) .body(ApiResponse.error(e.getError(), e.getMessage())); @@ -52,7 +58,7 @@ protected ResponseEntity handleCustomException(CustomException e) { @ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler(MethodArgumentNotValidException.class) - protected ResponseEntity handleConstraintDefinitionException(final MethodArgumentNotValidException e) { + protected ResponseEntity handleConstraintDefinitionException(final MethodArgumentNotValidException e, WebRequest request) { FieldError fieldError = e.getBindingResult().getFieldError(); Sentry.captureException(e); return ResponseEntity.status(e.getStatusCode()) @@ -60,7 +66,7 @@ protected ResponseEntity handleConstraintDefinitionException(final } @ExceptionHandler(MalformedURLException.class) - protected ResponseEntity handleConstraintDefinitionException(final MalformedURLException e) { + protected ResponseEntity handleConstraintDefinitionException(final MalformedURLException e, WebRequest request) { Sentry.captureException(e); return ResponseEntity.status(Error.MALFORMED_URL_EXEPTION.getErrorCode()) .body(ApiResponse.error(Error.MALFORMED_URL_EXEPTION, Error.MALFORMED_URL_EXEPTION.getMessage())); @@ -68,42 +74,49 @@ protected ResponseEntity handleConstraintDefinitionException(final @ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler(DateTimeParseException.class) - protected ResponseEntity handleDateTimeParseException(final DateTimeParseException e) { + protected ResponseEntity handleDateTimeParseException(final DateTimeParseException e, WebRequest request) { return ResponseEntity.status(Error.BAD_REQUEST_REMIND_TIME.getErrorCode()) .body(ApiResponse.error(Error.BAD_REQUEST_REMIND_TIME, Error.BAD_REQUEST_REMIND_TIME.getMessage())); } @ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler(HttpRequestMethodNotSupportedException.class) - protected ResponseEntity handleHttpRequestMethodNotSupportedException(final HttpRequestMethodNotSupportedException e) { + protected ResponseEntity handleHttpRequestMethodNotSupportedException(final HttpRequestMethodNotSupportedException e, WebRequest request) { return ResponseEntity.status(e.getStatusCode()) .body(ApiResponse.error(Error.REQUEST_METHOD_VALIDATION_EXCEPTION, Error.REQUEST_METHOD_VALIDATION_EXCEPTION.getMessage())); } @ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler(HttpMediaTypeNotSupportedException.class) - protected ResponseEntity handleHttpMediaTypeNotSupportedException(final HttpMediaTypeNotSupportedException e) { + protected ResponseEntity handleHttpMediaTypeNotSupportedException(final HttpMediaTypeNotSupportedException e, WebRequest request) { return ResponseEntity.status(e.getStatusCode()) .body(ApiResponse.error(Error.REQUEST_MEDIA_TYPE_VALIDATION_EXCEPTION, Error.REQUEST_MEDIA_TYPE_VALIDATION_EXCEPTION.getMessage())); } @ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler(MissingServletRequestParameterException.class) - protected ResponseEntity MissingServletRequestParameterException(final MissingServletRequestParameterException e) { + protected ResponseEntity MissingServletRequestParameterException(final MissingServletRequestParameterException e, WebRequest request) { return ResponseEntity.status(e.getStatusCode()) .body(ApiResponse.error(Error.BAD_REQUEST_VALIDATION, Error.BAD_REQUEST_VALIDATION.getMessage())); } + @ResponseStatus(HttpStatus.BAD_REQUEST) + @ExceptionHandler(NoResourceFoundException.class) + protected ResponseEntity ServletCrawlerException(final NoResourceFoundException e, WebRequest request) { + return ResponseEntity.status(e.getStatusCode()) + .body(ApiResponse.error(Error.BAD_REQUEST_VALIDATION, Error.BAD_REQUEST_VALIDATION.getMessage())); + } + @ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler(HttpMessageNotReadableException.class) - protected ResponseEntity MissingServletRequestParameterException(final HttpMessageNotReadableException e) { + protected ResponseEntity MissingServletRequestParameterException(final HttpMessageNotReadableException e, WebRequest request) { return ResponseEntity.status(Error.BAD_REQUEST_VALIDATION.getErrorCode()) .body(ApiResponse.error(Error.BAD_REQUEST_VALIDATION, Error.BAD_REQUEST_VALIDATION.getMessage())); } @ResponseStatus(HttpStatus.BAD_REQUEST) // 커스텀 validation 에러 핸들링. @ExceptionHandler(HandlerMethodValidationException.class) - protected ResponseEntity ConstraintViolationException(final HandlerMethodValidationException e) { + protected ResponseEntity ConstraintViolationException(final HandlerMethodValidationException e, WebRequest request) { Sentry.captureException(e); return ResponseEntity.status(Error.BAD_REQUEST_VALIDATION.getErrorCode()) .body(ApiResponse.error(Error.BAD_REQUEST_VALIDATION, Error.BAD_REQUEST_VALIDATION.getMessage())); @@ -111,7 +124,7 @@ protected ResponseEntity ConstraintViolationException(final Handler @ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler(UnknownHostException.class) - protected ResponseEntity UnknownHostException(final UnknownHostException e) { + protected ResponseEntity UnknownHostException(final UnknownHostException e, WebRequest request) { Sentry.captureException(e); return ResponseEntity.status(Error.BAD_REQUEST_VALIDATION.getErrorCode()) .body(ApiResponse.error(Error.BAD_REQUEST_VALIDATION, Error.BAD_REQUEST_VALIDATION.getMessage())); @@ -119,7 +132,7 @@ protected ResponseEntity UnknownHostException(final UnknownHostExce @ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler(MissingRequestHeaderException.class) - protected ResponseEntity MissingRequestHeaderException(final MissingRequestHeaderException e){ + protected ResponseEntity MissingRequestHeaderException(final MissingRequestHeaderException e, WebRequest request){ Sentry.captureException(e); return ResponseEntity.status(Error.BAD_REQUEST_VALIDATION.getErrorCode()) .body(ApiResponse.error(Error.BAD_REQUEST_VALIDATION, Error.BAD_REQUEST_VALIDATION.getMessage())); @@ -134,8 +147,10 @@ protected ResponseEntity MissingRequestHeaderException(final Missin @ExceptionHandler(Exception.class) protected ApiResponse handleException(final Exception error, final HttpServletRequest request) throws IOException { - slackApi.sendAlert(error, request); + // slackApi.sendAlert(error, request); Sentry.captureException(error); + discordMessageProvider.sendNotification( + new NotificationDto(NotificationType.ERROR,error,request.getRequestURI())); return ApiResponse.error(Error.INTERNAL_SERVER_ERROR); } diff --git a/linkmind/src/main/java/com/app/toaster/config/UserId.java b/linkmind/src/main/java/com/app/toaster/common/config/UserId.java similarity index 86% rename from linkmind/src/main/java/com/app/toaster/config/UserId.java rename to linkmind/src/main/java/com/app/toaster/common/config/UserId.java index 88bc0e8f..2b06b6cf 100644 --- a/linkmind/src/main/java/com/app/toaster/config/UserId.java +++ b/linkmind/src/main/java/com/app/toaster/common/config/UserId.java @@ -1,4 +1,4 @@ -package com.app.toaster.config; +package com.app.toaster.common.config; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; diff --git a/linkmind/src/main/java/com/app/toaster/config/UserIdResolver.java b/linkmind/src/main/java/com/app/toaster/common/config/UserIdResolver.java similarity index 94% rename from linkmind/src/main/java/com/app/toaster/config/UserIdResolver.java rename to linkmind/src/main/java/com/app/toaster/common/config/UserIdResolver.java index 2f2566e5..f1f1bbdf 100644 --- a/linkmind/src/main/java/com/app/toaster/config/UserIdResolver.java +++ b/linkmind/src/main/java/com/app/toaster/common/config/UserIdResolver.java @@ -1,5 +1,6 @@ -package com.app.toaster.config; +package com.app.toaster.common.config; +import com.app.toaster.common.config.jwt.JwtService; import org.springframework.core.MethodParameter; import org.springframework.stereotype.Component; import org.springframework.web.bind.support.WebDataBinderFactory; @@ -7,8 +8,6 @@ import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.ModelAndViewContainer; -import com.app.toaster.config.jwt.JwtService; - import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.constraints.NotNull; import lombok.RequiredArgsConstructor; diff --git a/linkmind/src/main/java/com/app/toaster/config/WebConfig.java b/linkmind/src/main/java/com/app/toaster/common/config/WebConfig.java similarity index 93% rename from linkmind/src/main/java/com/app/toaster/config/WebConfig.java rename to linkmind/src/main/java/com/app/toaster/common/config/WebConfig.java index c1e66998..dc9e16ab 100644 --- a/linkmind/src/main/java/com/app/toaster/config/WebConfig.java +++ b/linkmind/src/main/java/com/app/toaster/common/config/WebConfig.java @@ -1,4 +1,4 @@ -package com.app.toaster.config; +package com.app.toaster.common.config; import java.util.List; diff --git a/linkmind/src/main/java/com/app/toaster/config/jwt/JwtService.java b/linkmind/src/main/java/com/app/toaster/common/config/jwt/JwtService.java similarity index 98% rename from linkmind/src/main/java/com/app/toaster/config/jwt/JwtService.java rename to linkmind/src/main/java/com/app/toaster/common/config/jwt/JwtService.java index e87d86e2..a7770db4 100644 --- a/linkmind/src/main/java/com/app/toaster/config/jwt/JwtService.java +++ b/linkmind/src/main/java/com/app/toaster/common/config/jwt/JwtService.java @@ -1,4 +1,4 @@ -package com.app.toaster.config.jwt; +package com.app.toaster.common.config.jwt; import static io.jsonwebtoken.Jwts.*; diff --git a/linkmind/src/main/java/com/app/toaster/domain/BaseTimeEntity.java b/linkmind/src/main/java/com/app/toaster/common/domain/BaseTimeEntity.java similarity index 94% rename from linkmind/src/main/java/com/app/toaster/domain/BaseTimeEntity.java rename to linkmind/src/main/java/com/app/toaster/common/domain/BaseTimeEntity.java index b261aa23..f5be0c98 100644 --- a/linkmind/src/main/java/com/app/toaster/domain/BaseTimeEntity.java +++ b/linkmind/src/main/java/com/app/toaster/common/domain/BaseTimeEntity.java @@ -1,4 +1,4 @@ -package com.app.toaster.domain; +package com.app.toaster.common.domain; import jakarta.persistence.EntityListeners; diff --git a/linkmind/src/main/java/com/app/toaster/domain/IntegerListConverter.java b/linkmind/src/main/java/com/app/toaster/common/domain/IntegerListConverter.java similarity index 95% rename from linkmind/src/main/java/com/app/toaster/domain/IntegerListConverter.java rename to linkmind/src/main/java/com/app/toaster/common/domain/IntegerListConverter.java index 6d151397..6c5946be 100644 --- a/linkmind/src/main/java/com/app/toaster/domain/IntegerListConverter.java +++ b/linkmind/src/main/java/com/app/toaster/common/domain/IntegerListConverter.java @@ -1,4 +1,4 @@ -package com.app.toaster.domain; +package com.app.toaster.common.domain; import jakarta.persistence.AttributeConverter; import jakarta.persistence.Converter; diff --git a/linkmind/src/main/java/com/app/toaster/config/JpaQueryFactoryConfig.java b/linkmind/src/main/java/com/app/toaster/config/JpaQueryFactoryConfig.java deleted file mode 100644 index b7bbfd92..00000000 --- a/linkmind/src/main/java/com/app/toaster/config/JpaQueryFactoryConfig.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.app.toaster.config; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -import com.querydsl.jpa.impl.JPAQueryFactory; - -import jakarta.persistence.EntityManager; - -@Configuration -public class JpaQueryFactoryConfig { - - @Bean - JPAQueryFactory jpaQueryFactory(EntityManager em) { - return new JPAQueryFactory(em); - } -} \ No newline at end of file diff --git a/linkmind/src/main/java/com/app/toaster/controller/request/toast/DeleteToastDto.java b/linkmind/src/main/java/com/app/toaster/controller/request/toast/DeleteToastDto.java deleted file mode 100644 index fa5b054a..00000000 --- a/linkmind/src/main/java/com/app/toaster/controller/request/toast/DeleteToastDto.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.app.toaster.controller.request.toast; - -public record DeleteToastDto(Long toastId) { -} diff --git a/linkmind/src/main/java/com/app/toaster/controller/request/toast/OgRequestDto.java b/linkmind/src/main/java/com/app/toaster/controller/request/toast/OgRequestDto.java deleted file mode 100644 index 1523a76e..00000000 --- a/linkmind/src/main/java/com/app/toaster/controller/request/toast/OgRequestDto.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.app.toaster.controller.request.toast; - -public record OgRequestDto(String linkUrl) { -} diff --git a/linkmind/src/main/java/com/app/toaster/controller/request/toast/SaveToastDto.java b/linkmind/src/main/java/com/app/toaster/controller/request/toast/SaveToastDto.java deleted file mode 100644 index 34eeb1e2..00000000 --- a/linkmind/src/main/java/com/app/toaster/controller/request/toast/SaveToastDto.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.app.toaster.controller.request.toast; - -import java.util.List; - -import com.app.toaster.controller.valid.Severity; -import com.app.toaster.controller.valid.TitleValid; - -public record SaveToastDto(String linkUrl, Long categoryId) { -} diff --git a/linkmind/src/main/java/com/app/toaster/controller/request/toast/UpdateToastDto.java b/linkmind/src/main/java/com/app/toaster/controller/request/toast/UpdateToastDto.java deleted file mode 100644 index 0126eafe..00000000 --- a/linkmind/src/main/java/com/app/toaster/controller/request/toast/UpdateToastDto.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.app.toaster.controller.request.toast; - -import com.app.toaster.controller.valid.Severity; -import com.app.toaster.controller.valid.TitleValid; - -import jakarta.validation.constraints.NotNull; - -public record UpdateToastDto(Long toastId, @TitleValid(payload = Severity.Error.class) @NotNull String title) { - public static UpdateToastDto of(Long toastId, String title){ - return new UpdateToastDto(toastId,title); - } -} diff --git a/linkmind/src/main/java/com/app/toaster/controller/response/category/GetCategoryResponseDto.java b/linkmind/src/main/java/com/app/toaster/controller/response/category/GetCategoryResponseDto.java deleted file mode 100644 index f274ccf6..00000000 --- a/linkmind/src/main/java/com/app/toaster/controller/response/category/GetCategoryResponseDto.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.app.toaster.controller.response.category; - -import com.app.toaster.controller.response.auth.TokenResponseDto; -import com.app.toaster.controller.response.toast.ToastDto; -import com.app.toaster.domain.Toast; -import lombok.Builder; - -import java.util.ArrayList; -import java.util.List; - -@Builder -public record GetCategoryResponseDto( - int allToastNum, - List toastListDto -){ - -} diff --git a/linkmind/src/main/java/com/app/toaster/exception/Error.java b/linkmind/src/main/java/com/app/toaster/exception/Error.java index 4c356050..164c3f96 100644 --- a/linkmind/src/main/java/com/app/toaster/exception/Error.java +++ b/linkmind/src/main/java/com/app/toaster/exception/Error.java @@ -20,6 +20,7 @@ public enum Error { NOT_FOUND_IMAGE_EXCEPTION(HttpStatus.NOT_FOUND, "s3 서비스에서 이미지를 찾을 수 없습니다."), NOT_FOUND_TOAST_FILTER(HttpStatus.NOT_FOUND, "유효하지 않은 필터입니다."), NOT_FOUND_TIMER(HttpStatus.NOT_FOUND, "찾을 수 없는 타이머입니다."), + NOT_FOUND_POPUP_EXCEPTION(HttpStatus.NOT_FOUND, "유효하지 않은 팝업입니다."), /** * 400 BAD REQUEST EXCEPTION @@ -69,6 +70,7 @@ public enum Error { CREATE_PUBLIC_KEY_EXCEPTION(HttpStatus.INTERNAL_SERVER_ERROR, "publickey 생성 과정 중 문제가 발생했습니다."), FAIL_TO_SEND_PUSH_ALARM(HttpStatus.INTERNAL_SERVER_ERROR, "다수기기 푸시메시지 전송 실패"), FAIL_TO_SEND_SQS(HttpStatus.INTERNAL_SERVER_ERROR, "sqs 전송 실패"), + INVALID_DISCORD_MESSAGE(HttpStatus.INTERNAL_SERVER_ERROR, "디스코드 알림 전송 실패"), CREATE_TOAST_PROCCESS_EXCEPTION(HttpStatus.INTERNAL_SERVER_ERROR, "토스트 저장 중 문제가 발생했습니다. 카테고리 또는 s3 관련 문제로 예상됩니다.") ; diff --git a/linkmind/src/main/java/com/app/toaster/exception/Success.java b/linkmind/src/main/java/com/app/toaster/exception/Success.java index 53fdb5f9..34055ba3 100644 --- a/linkmind/src/main/java/com/app/toaster/exception/Success.java +++ b/linkmind/src/main/java/com/app/toaster/exception/Success.java @@ -24,6 +24,8 @@ public enum Success { GET_LINKS_SUCCESS(HttpStatus.OK, "이주의 링크 조회 성공"), GET_SITES_SUCCESS(HttpStatus.OK, "추천 사이트 조회 성공"), GET_SETTINGS_SUCCESS(HttpStatus.OK, "설정 페이지 조회 성공"), + GET_POPUP_SUCCESS(HttpStatus.OK, "팝업 정보 조회 성공"), + GET_RECENT_TOAST_SUCCESS(HttpStatus.OK, "최근 저장한 토스트 조회 성공"), GET_CATEORIES_SUCCESS(HttpStatus.OK, "전체 카테고리 조회 성공"), GET_CATEORY_SUCCESS(HttpStatus.OK, "세부 카테고리 조회 성공"), @@ -46,12 +48,14 @@ public enum Success { UPDATE_ISREAD_SUCCESS(HttpStatus.OK, "열람여부 수정 완료"), UPDATE_CATEGORY_TITLE_SUCCESS(HttpStatus.OK, "카테고리 수정 완료"), + MOVE_CATEGORY_SUCCESS(HttpStatus.OK, "카테고리 이동 성공"), UPDATE_TIMER_DATETIME_SUCCESS(HttpStatus.OK, "타이머 시간/날짜 수정 완료"), UPDATE_TIMER_COMMENT_SUCCESS(HttpStatus.OK, "타이머 코멘트 수정 완료"), CHANGE_TIMER_ALARM_SUCCESS(HttpStatus.OK, "타이머 알람여부 수정 완료"), PUSH_ALARM_PERIODIC_SUCCESS(HttpStatus.OK, "푸시알림 활성에 성공했습니다."), PUSH_ALARM_SUCCESS(HttpStatus.OK, "푸시알림 전송에 성공했습니다."), CLEAR_SCHEDULED_TASKS_SUCCESS(HttpStatus.OK, "스케줄러에서 예약된 작업을 제거했습니다."), + UPDATE_POPUP_SUCCESS(HttpStatus.OK, "팝업 데이터 수정 성공"), /** diff --git a/linkmind/src/main/java/com/app/toaster/external/client/discord/DiscordClient.java b/linkmind/src/main/java/com/app/toaster/external/client/discord/DiscordClient.java new file mode 100644 index 00000000..b56c3684 --- /dev/null +++ b/linkmind/src/main/java/com/app/toaster/external/client/discord/DiscordClient.java @@ -0,0 +1,14 @@ +package com.app.toaster.external.client.discord; + +import java.net.URI; + +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; + +@FeignClient(name = "discord-feign-client", url = "URI") +public interface DiscordClient { + @PostMapping(produces = MediaType.APPLICATION_JSON_VALUE) + void sendMessage(URI uri, @RequestBody DiscordMessage discordMessage); +} \ No newline at end of file diff --git a/linkmind/src/main/java/com/app/toaster/external/client/discord/DiscordMessage.java b/linkmind/src/main/java/com/app/toaster/external/client/discord/DiscordMessage.java new file mode 100644 index 00000000..ba64baf7 --- /dev/null +++ b/linkmind/src/main/java/com/app/toaster/external/client/discord/DiscordMessage.java @@ -0,0 +1,29 @@ +package com.app.toaster.external.client.discord; + +import java.util.List; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Builder +@AllArgsConstructor(access = AccessLevel.PROTECTED) +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Getter +public class DiscordMessage { + + private String content; + private List embeds; + + @Builder + @AllArgsConstructor(access = AccessLevel.PROTECTED) + @NoArgsConstructor(access = AccessLevel.PROTECTED) + @Getter + public static class Embed { + + private String title; + private String description; + } +} \ No newline at end of file diff --git a/linkmind/src/main/java/com/app/toaster/external/client/discord/DiscordMessageProvider.java b/linkmind/src/main/java/com/app/toaster/external/client/discord/DiscordMessageProvider.java new file mode 100644 index 00000000..90f23200 --- /dev/null +++ b/linkmind/src/main/java/com/app/toaster/external/client/discord/DiscordMessageProvider.java @@ -0,0 +1,115 @@ +package com.app.toaster.external.client.discord; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.net.URI; +import java.time.LocalDateTime; +import java.util.Arrays; +import java.util.List; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Component; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; +import org.springframework.web.context.request.ServletWebRequest; +import org.springframework.web.context.request.WebRequest; + +import com.app.toaster.user.infrastructure.UserRepository; + +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +@RequiredArgsConstructor +@Component +@Slf4j +public class DiscordMessageProvider { + private final DiscordClient discordClient; + private final UserRepository userRepository; + private final Environment environment; + + @Value("${discord.webhook-url-error}") + private String webhookUrlError; + + + @Value("${discord.webhook-url-sign}") + private String webhookUrlSign; + + @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) + public void sendNotification(NotificationDto notification) { + if (!Arrays.asList(environment.getActiveProfiles()).contains("local")) { + try { + switch (notification.type()){ + case ERROR -> discordClient.sendMessage(URI.create(webhookUrlError), createErrorMessage(notification.e(), notification.request())); + case SIGNUP -> discordClient.sendMessage(URI.create(webhookUrlSign), createSignUpMessage()); + } + } catch (Exception error) { + log.warn("discord notification fail : " + error); + } + } + } + + private DiscordMessage createSignUpMessage() { + return DiscordMessage.builder() + .content("# 😍 회원가입 이벤트가 발생했습니다.") + .embeds( + List.of( + DiscordMessage.Embed.builder() + .title("ℹ️ 회원가입 정보") + .description( + "### 🕖 발생 시간\n" + + LocalDateTime.now() + + "\n" + + "### 📜 유저 가입 정보\n" + + "토스터의 " + userRepository.count() + "번째 유저가 생성되었습니다!! ❤️" + + "\n") + .build() + ) + ) + .build(); + } + + + private DiscordMessage createErrorMessage(Exception e, String requestUrl) { + return DiscordMessage.builder() + .content("# 🚨 삐용삐용 에러났어요 에러났어요") + .embeds( + List.of( + DiscordMessage.Embed.builder() + .title("ℹ️ 에러 정보") + .description( + "### 🕖 발생 시간\n" + + LocalDateTime.now() + + "\n" + + "### 🔗 요청 URL\n" + + requestUrl + + "\n" + + "### 📄 Stack Trace\n" + + "```\n" + + getStackTrace(e).substring(0, 1000) + + "\n```") + .build() + ) + ) + .build(); + } + + private String createRequestFullPath(WebRequest webRequest) { + HttpServletRequest request = ((ServletWebRequest) webRequest).getRequest(); + String fullPath = request.getMethod() + " " + request.getRequestURL(); + + String queryString = request.getQueryString(); + if (queryString != null) { + fullPath += "?" + queryString; + } + + return fullPath; + } + + private String getStackTrace(Exception e) { + StringWriter stringWriter = new StringWriter(); + e.printStackTrace(new PrintWriter(stringWriter)); + return stringWriter.toString(); + } +} \ No newline at end of file diff --git a/linkmind/src/main/java/com/app/toaster/external/client/discord/NotificationDto.java b/linkmind/src/main/java/com/app/toaster/external/client/discord/NotificationDto.java new file mode 100644 index 00000000..5a745c74 --- /dev/null +++ b/linkmind/src/main/java/com/app/toaster/external/client/discord/NotificationDto.java @@ -0,0 +1,9 @@ +package com.app.toaster.external.client.discord; + + +public record NotificationDto( + NotificationType type, + Exception e, + String request +) { +} diff --git a/linkmind/src/main/java/com/app/toaster/external/client/discord/NotificationType.java b/linkmind/src/main/java/com/app/toaster/external/client/discord/NotificationType.java new file mode 100644 index 00000000..ac154ab0 --- /dev/null +++ b/linkmind/src/main/java/com/app/toaster/external/client/discord/NotificationType.java @@ -0,0 +1,6 @@ +package com.app.toaster.external.client.discord; + +public enum NotificationType { + ERROR, + SIGNUP +} diff --git a/linkmind/src/main/java/com/app/toaster/external/client/slack/SlackApi.java b/linkmind/src/main/java/com/app/toaster/external/client/slack/SlackApi.java index 6c352db8..eff05bbd 100644 --- a/linkmind/src/main/java/com/app/toaster/external/client/slack/SlackApi.java +++ b/linkmind/src/main/java/com/app/toaster/external/client/slack/SlackApi.java @@ -10,7 +10,7 @@ import org.springframework.stereotype.Component; import com.app.toaster.exception.Success; -import com.app.toaster.infrastructure.UserRepository; +import com.app.toaster.user.infrastructure.UserRepository; import com.slack.api.Slack; import com.slack.api.model.block.Blocks; import com.slack.api.model.block.LayoutBlock; diff --git a/linkmind/src/main/java/com/app/toaster/domain/Link.java b/linkmind/src/main/java/com/app/toaster/link/domain/Link.java similarity index 94% rename from linkmind/src/main/java/com/app/toaster/domain/Link.java rename to linkmind/src/main/java/com/app/toaster/link/domain/Link.java index 48188126..577d0a88 100644 --- a/linkmind/src/main/java/com/app/toaster/domain/Link.java +++ b/linkmind/src/main/java/com/app/toaster/link/domain/Link.java @@ -1,4 +1,4 @@ -package com.app.toaster.domain; +package com.app.toaster.link.domain; import com.fasterxml.jackson.annotation.JsonRootName; import com.fasterxml.jackson.databind.annotation.JsonNaming; @@ -17,7 +17,7 @@ public class Link { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long Id; + private Long id; private String title; diff --git a/linkmind/src/main/java/com/app/toaster/infrastructure/LinkRepository.java b/linkmind/src/main/java/com/app/toaster/link/infrastructure/LinkRepository.java similarity index 71% rename from linkmind/src/main/java/com/app/toaster/infrastructure/LinkRepository.java rename to linkmind/src/main/java/com/app/toaster/link/infrastructure/LinkRepository.java index 6a63beba..b99e7a44 100644 --- a/linkmind/src/main/java/com/app/toaster/infrastructure/LinkRepository.java +++ b/linkmind/src/main/java/com/app/toaster/link/infrastructure/LinkRepository.java @@ -1,13 +1,10 @@ -package com.app.toaster.infrastructure; +package com.app.toaster.link.infrastructure; -import com.app.toaster.domain.Category; -import com.app.toaster.domain.Link; -import com.app.toaster.domain.User; +import com.app.toaster.link.domain.Link; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; -import java.util.ArrayList; import java.util.List; @Repository diff --git a/linkmind/src/main/java/com/app/toaster/service/link/LinkService.java b/linkmind/src/main/java/com/app/toaster/link/service/LinkService.java similarity index 75% rename from linkmind/src/main/java/com/app/toaster/service/link/LinkService.java rename to linkmind/src/main/java/com/app/toaster/link/service/LinkService.java index 4509e41a..adcc03d4 100644 --- a/linkmind/src/main/java/com/app/toaster/service/link/LinkService.java +++ b/linkmind/src/main/java/com/app/toaster/link/service/LinkService.java @@ -1,18 +1,15 @@ -package com.app.toaster.service.link; +package com.app.toaster.link.service; -import com.app.toaster.controller.response.toast.WeekLinkDto; -import com.app.toaster.domain.Link; -import com.app.toaster.infrastructure.LinkRepository; +import com.app.toaster.toast.controller.response.WeekLinkDto; +import com.app.toaster.link.domain.Link; +import com.app.toaster.link.infrastructure.LinkRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.time.Duration; import java.time.LocalDateTime; -import java.time.Period; -import java.util.ArrayList; import java.util.List; -import java.util.stream.Collectors; @Service @RequiredArgsConstructor diff --git a/linkmind/src/main/java/com/app/toaster/controller/MainController.java b/linkmind/src/main/java/com/app/toaster/mainpage/controller/MainController.java similarity index 59% rename from linkmind/src/main/java/com/app/toaster/controller/MainController.java rename to linkmind/src/main/java/com/app/toaster/mainpage/controller/MainController.java index 5c8fc860..95ac49ac 100644 --- a/linkmind/src/main/java/com/app/toaster/controller/MainController.java +++ b/linkmind/src/main/java/com/app/toaster/mainpage/controller/MainController.java @@ -1,14 +1,11 @@ -package com.app.toaster.controller; +package com.app.toaster.mainpage.controller; -import com.app.toaster.config.UserId; +import com.app.toaster.common.config.UserId; import org.springframework.web.bind.annotation.*; import com.app.toaster.common.dto.ApiResponse; -import com.app.toaster.config.UserId; -import com.app.toaster.controller.valid.TitleValid; -import com.app.toaster.service.UserService; -import com.app.toaster.service.search.SearchService; -import com.app.toaster.service.toast.ToastService; +import com.app.toaster.utils.valid.TitleValid; +import com.app.toaster.search.service.SearchService; import lombok.RequiredArgsConstructor; diff --git a/linkmind/src/main/java/com/app/toaster/controller/response/main/MainPageResponseDto.java b/linkmind/src/main/java/com/app/toaster/mainpage/controller/response/MainPageResponseDto.java similarity index 62% rename from linkmind/src/main/java/com/app/toaster/controller/response/main/MainPageResponseDto.java rename to linkmind/src/main/java/com/app/toaster/mainpage/controller/response/MainPageResponseDto.java index d094f004..1643261a 100644 --- a/linkmind/src/main/java/com/app/toaster/controller/response/main/MainPageResponseDto.java +++ b/linkmind/src/main/java/com/app/toaster/mainpage/controller/response/MainPageResponseDto.java @@ -1,6 +1,6 @@ -package com.app.toaster.controller.response.main; +package com.app.toaster.mainpage.controller.response; -import com.app.toaster.controller.response.category.CategoryResponse; +import com.app.toaster.category.controller.response.CategoryResponse; import lombok.Builder; diff --git a/linkmind/src/main/java/com/app/toaster/controller/response/parse/OgResponse.java b/linkmind/src/main/java/com/app/toaster/parse/controller/response/OgResponse.java similarity index 80% rename from linkmind/src/main/java/com/app/toaster/controller/response/parse/OgResponse.java rename to linkmind/src/main/java/com/app/toaster/parse/controller/response/OgResponse.java index c7960a9e..5f705545 100644 --- a/linkmind/src/main/java/com/app/toaster/controller/response/parse/OgResponse.java +++ b/linkmind/src/main/java/com/app/toaster/parse/controller/response/OgResponse.java @@ -1,4 +1,4 @@ -package com.app.toaster.controller.response.parse; +package com.app.toaster.parse.controller.response; public record OgResponse(String titleAdvanced, String imageAdvanced) { public static OgResponse of(String titleAdvanced, String imageAdvanced){ diff --git a/linkmind/src/main/java/com/app/toaster/service/parse/ParsingService.java b/linkmind/src/main/java/com/app/toaster/parse/service/ParsingService.java similarity index 92% rename from linkmind/src/main/java/com/app/toaster/service/parse/ParsingService.java rename to linkmind/src/main/java/com/app/toaster/parse/service/ParsingService.java index 2df81e58..758674ff 100644 --- a/linkmind/src/main/java/com/app/toaster/service/parse/ParsingService.java +++ b/linkmind/src/main/java/com/app/toaster/parse/service/ParsingService.java @@ -1,11 +1,8 @@ -package com.app.toaster.service.parse; +package com.app.toaster.parse.service; import java.io.IOException; import java.net.ConnectException; import java.net.MalformedURLException; -import java.nio.charset.StandardCharsets; -import java.util.Base64; -import java.util.List; import javax.net.ssl.SSLHandshakeException; @@ -18,13 +15,11 @@ // import com.app.toaster.external.client.aws.S3Service; -import com.app.toaster.controller.response.parse.OgResponse; +import com.app.toaster.parse.controller.response.OgResponse; import com.app.toaster.exception.Error; import com.app.toaster.exception.model.BadRequestException; import com.app.toaster.exception.model.CustomException; -import com.app.toaster.external.client.aws.AWSConfig; -import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @Service diff --git a/linkmind/src/main/java/com/app/toaster/popup/controller/PopupController.java b/linkmind/src/main/java/com/app/toaster/popup/controller/PopupController.java new file mode 100644 index 00000000..b90a16a7 --- /dev/null +++ b/linkmind/src/main/java/com/app/toaster/popup/controller/PopupController.java @@ -0,0 +1,39 @@ +package com.app.toaster.popup.controller; + +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.app.toaster.popup.controller.request.PopUpRequestDto; +import com.app.toaster.popup.controller.response.InvisibleResponseDto; +import com.app.toaster.popup.controller.response.PopupResponseDto; +import com.app.toaster.popup.service.PopupService; +import com.app.toaster.common.dto.ApiResponse; +import com.app.toaster.common.config.UserId; +import com.app.toaster.exception.Success; + +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/v2/popup") +@Validated +public class PopupController { + private final PopupService popupService; + + + @PatchMapping + public ApiResponse updateInvisible(@UserId Long userId, @RequestBody @Valid PopUpRequestDto popUpRequestDto){ + return ApiResponse.success(Success.UPDATE_POPUP_SUCCESS, popupService.updatePopupInvisible(userId,popUpRequestDto)); + } + + @GetMapping + public ApiResponse getPopUpInformation(@UserId Long userId){ + return ApiResponse.success(Success.GET_POPUP_SUCCESS, popupService.findPopupInformation(userId)); + } + +} diff --git a/linkmind/src/main/java/com/app/toaster/popup/controller/request/PopUpRequestDto.java b/linkmind/src/main/java/com/app/toaster/popup/controller/request/PopUpRequestDto.java new file mode 100644 index 00000000..64e1c950 --- /dev/null +++ b/linkmind/src/main/java/com/app/toaster/popup/controller/request/PopUpRequestDto.java @@ -0,0 +1,10 @@ +package com.app.toaster.popup.controller.request; + +import jakarta.validation.constraints.NotNull; + +public record PopUpRequestDto( + Long popupId, + @NotNull + Long hideDate +) { +} diff --git a/linkmind/src/main/java/com/app/toaster/popup/controller/response/InvisibleResponseDto.java b/linkmind/src/main/java/com/app/toaster/popup/controller/response/InvisibleResponseDto.java new file mode 100644 index 00000000..43b34562 --- /dev/null +++ b/linkmind/src/main/java/com/app/toaster/popup/controller/response/InvisibleResponseDto.java @@ -0,0 +1,12 @@ +package com.app.toaster.popup.controller.response; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; + +import org.springframework.format.annotation.DateTimeFormat; + +public record InvisibleResponseDto(Long popupId, String hideUntil) { + public static InvisibleResponseDto of(Long popupId, LocalDate hideUntil){ + return new InvisibleResponseDto(popupId, hideUntil.format(DateTimeFormatter.ofPattern("YYYY-MM-dd"))); + } +} diff --git a/linkmind/src/main/java/com/app/toaster/popup/controller/response/PopupResponseDto.java b/linkmind/src/main/java/com/app/toaster/popup/controller/response/PopupResponseDto.java new file mode 100644 index 00000000..de553c44 --- /dev/null +++ b/linkmind/src/main/java/com/app/toaster/popup/controller/response/PopupResponseDto.java @@ -0,0 +1,12 @@ +package com.app.toaster.popup.controller.response; + +import java.util.List; + +import com.app.toaster.popup.entity.Popup; + +public record PopupResponseDto(List popupList) { + + public static PopupResponseDto from(List popupList){ + return new PopupResponseDto(popupList); + } +} diff --git a/linkmind/src/main/java/com/app/toaster/popup/entity/Popup.java b/linkmind/src/main/java/com/app/toaster/popup/entity/Popup.java new file mode 100644 index 00000000..f9015825 --- /dev/null +++ b/linkmind/src/main/java/com/app/toaster/popup/entity/Popup.java @@ -0,0 +1,50 @@ +package com.app.toaster.popup.entity; + +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.List; + +import jakarta.persistence.CascadeType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.OneToMany; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class Popup { + + @Id + @Column(name = "popup_id") + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(columnDefinition = "TEXT", name = "image") + private String image; + + private LocalDate activeStartDate; + + private LocalDate activeEndDate; + + @Column(name = "link_url") + private String linkUrl; + + @Builder + private Popup(String image, LocalDate activeStartDate, LocalDate activeEndDate, String linkUrl) { + this.image = image; + this.activeStartDate = activeStartDate; + this.activeEndDate = activeEndDate; + this.linkUrl = linkUrl; + } + + public boolean isActivePopup(LocalDate today){ + return !today.isBefore(activeStartDate)&&!today.isAfter(activeEndDate); + } +} diff --git a/linkmind/src/main/java/com/app/toaster/popup/entity/PopupInvisibleManager.java b/linkmind/src/main/java/com/app/toaster/popup/entity/PopupInvisibleManager.java new file mode 100644 index 00000000..07995a1f --- /dev/null +++ b/linkmind/src/main/java/com/app/toaster/popup/entity/PopupInvisibleManager.java @@ -0,0 +1,54 @@ +package com.app.toaster.popup.entity; + +import java.time.LocalDate; +import java.time.LocalDateTime; + +import org.hibernate.annotations.OnDelete; +import org.hibernate.annotations.OnDeleteAction; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +/** + * 유저를 엮을것인가? 그러기에는 userId만 필요함. + * popup을 엮을 것인가? -> popup이 없어지면 popupmanager도 삭제. + * 요구사항 추가: 1일 안보기 + 7일 안보기 + */ +@Entity +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class PopupInvisibleManager { + + @Id + @Column(name = "popup_manager_id") + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private Long userId; + + @ManyToOne + @OnDelete(action = OnDeleteAction.CASCADE) + @JoinColumn(name = "popup_id") + private Popup popup; + + private LocalDate hideDateUntil; + + @Builder + private PopupInvisibleManager(Long userId, Popup popup, boolean wantToInvisible) { + this.userId = userId; + this.popup = popup; + } + + public void updateInvisible(LocalDate untilDate){ + this.hideDateUntil = untilDate; // 계산해서 넘겨준 untilDate로 바꿈. + } +} diff --git a/linkmind/src/main/java/com/app/toaster/popup/infrastructure/PopupManagerRepository.java b/linkmind/src/main/java/com/app/toaster/popup/infrastructure/PopupManagerRepository.java new file mode 100644 index 00000000..755b1f38 --- /dev/null +++ b/linkmind/src/main/java/com/app/toaster/popup/infrastructure/PopupManagerRepository.java @@ -0,0 +1,16 @@ +package com.app.toaster.popup.infrastructure; + +import java.util.List; +import java.util.Optional; + +import org.springframework.data.jpa.repository.JpaRepository; + +import com.app.toaster.popup.entity.Popup; +import com.app.toaster.popup.entity.PopupInvisibleManager; + +public interface PopupManagerRepository extends JpaRepository { + List findByUserId(Long userId); + Optional findByUserIdAndPopup(Long userId, Popup popup); + + void deleteAllByUserId(Long userId); +} diff --git a/linkmind/src/main/java/com/app/toaster/popup/infrastructure/PopupRepository.java b/linkmind/src/main/java/com/app/toaster/popup/infrastructure/PopupRepository.java new file mode 100644 index 00000000..df26cd32 --- /dev/null +++ b/linkmind/src/main/java/com/app/toaster/popup/infrastructure/PopupRepository.java @@ -0,0 +1,8 @@ +package com.app.toaster.popup.infrastructure; + +import org.springframework.data.jpa.repository.JpaRepository; + +import com.app.toaster.popup.entity.Popup; + +public interface PopupRepository extends JpaRepository { +} diff --git a/linkmind/src/main/java/com/app/toaster/popup/service/PopupService.java b/linkmind/src/main/java/com/app/toaster/popup/service/PopupService.java new file mode 100644 index 00000000..c3107f3b --- /dev/null +++ b/linkmind/src/main/java/com/app/toaster/popup/service/PopupService.java @@ -0,0 +1,78 @@ +package com.app.toaster.popup.service; + +import java.time.LocalDate; +import java.time.temporal.TemporalAmount; +import java.util.List; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.app.toaster.exception.Error; +import com.app.toaster.exception.model.BadRequestException; +import com.app.toaster.exception.model.NotFoundException; +import com.app.toaster.popup.controller.request.PopUpRequestDto; +import com.app.toaster.popup.controller.response.InvisibleResponseDto; +import com.app.toaster.popup.controller.response.PopupResponseDto; +import com.app.toaster.popup.entity.Popup; +import com.app.toaster.popup.entity.PopupInvisibleManager; +import com.app.toaster.popup.infrastructure.PopupManagerRepository; +import com.app.toaster.popup.infrastructure.PopupRepository; + +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +@Transactional +public class PopupService { + private final PopupManagerRepository popupManagerRepository; + private final PopupRepository popupRepository; + + public InvisibleResponseDto updatePopupInvisible(Long userId, PopUpRequestDto popUpRequestDto) { + Popup popup = findActivePopup(popUpRequestDto.popupId()); + + PopupInvisibleManager manager = findManagerWithUserIdAndPopup(userId, popup); + LocalDate today = LocalDate.now(); + + manager.updateInvisible(today.plusDays(popUpRequestDto.hideDate())); + return InvisibleResponseDto.of(popup.getId(), manager.getHideDateUntil()); + } + + @Transactional(readOnly = true) + public PopupResponseDto findPopupInformation(Long userId) { + List invisibleManagerList = findInvisiblePopupIdList(userId); + return PopupResponseDto.from(popupRepository.findAll().stream() + .filter((popup) -> popup.isActivePopup(LocalDate.now())) + .filter(popup -> !invisibleManagerList.contains(popup.getId())) + .toList()); + } + + private Popup findActivePopup(Long id) { + LocalDate today = LocalDate.now(); + Popup popup = popupRepository.findById(id) + .orElseThrow(()-> new NotFoundException(Error.NOT_FOUND_POPUP_EXCEPTION, Error.NOT_FOUND_POPUP_EXCEPTION.getMessage())); + if (!popup.isActivePopup(today)){ + throw new BadRequestException(Error.BAD_REQUEST_ID, Error.BAD_REQUEST_ID.getMessage()); + } + return popup; + } + + private PopupInvisibleManager findManagerWithUserIdAndPopup(Long userId, Popup popup) { + return popupManagerRepository.findByUserIdAndPopup(userId, popup) + .orElseGet(() -> + popupManagerRepository.save(PopupInvisibleManager.builder() + .userId(userId) + .popup(popup) + .build()) + ); + } + + private List findInvisiblePopupIdList(Long userId) { + return popupManagerRepository.findByUserId(userId).stream() + .filter( + (manager) -> manager.getHideDateUntil().isAfter(LocalDate.now()) //오늘 날짜는 보여야지 + ) + .map(PopupInvisibleManager::getPopup) + .map(Popup::getId) + .toList(); + } +} diff --git a/linkmind/src/main/java/com/app/toaster/controller/RecommendSiteController.java b/linkmind/src/main/java/com/app/toaster/recommendsite/controller/RecommendSiteController.java similarity index 74% rename from linkmind/src/main/java/com/app/toaster/controller/RecommendSiteController.java rename to linkmind/src/main/java/com/app/toaster/recommendsite/controller/RecommendSiteController.java index 0c742977..38b5a0eb 100644 --- a/linkmind/src/main/java/com/app/toaster/controller/RecommendSiteController.java +++ b/linkmind/src/main/java/com/app/toaster/recommendsite/controller/RecommendSiteController.java @@ -1,13 +1,12 @@ -package com.app.toaster.controller; +package com.app.toaster.recommendsite.controller; import com.app.toaster.common.dto.ApiResponse; -import com.app.toaster.config.UserId; -import com.app.toaster.domain.RecommendSite; +import com.app.toaster.common.config.UserId; +import com.app.toaster.recommendsite.domain.RecommendSite; import com.app.toaster.exception.Success; -import com.app.toaster.service.recommendSite.RecommendSiteService; +import com.app.toaster.recommendsite.service.RecommendSiteService; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import java.util.List; diff --git a/linkmind/src/main/java/com/app/toaster/domain/RecommendSite.java b/linkmind/src/main/java/com/app/toaster/recommendsite/domain/RecommendSite.java similarity index 92% rename from linkmind/src/main/java/com/app/toaster/domain/RecommendSite.java rename to linkmind/src/main/java/com/app/toaster/recommendsite/domain/RecommendSite.java index 84af147d..ed392a9e 100644 --- a/linkmind/src/main/java/com/app/toaster/domain/RecommendSite.java +++ b/linkmind/src/main/java/com/app/toaster/recommendsite/domain/RecommendSite.java @@ -1,4 +1,4 @@ -package com.app.toaster.domain; +package com.app.toaster.recommendsite.domain; import jakarta.persistence.*; import lombok.AccessLevel; @@ -6,8 +6,6 @@ import lombok.Getter; import lombok.NoArgsConstructor; -import java.time.LocalDateTime; - @Getter @Entity diff --git a/linkmind/src/main/java/com/app/toaster/domain/Topic.java b/linkmind/src/main/java/com/app/toaster/recommendsite/domain/Topic.java similarity index 83% rename from linkmind/src/main/java/com/app/toaster/domain/Topic.java rename to linkmind/src/main/java/com/app/toaster/recommendsite/domain/Topic.java index 09e36124..d20673dd 100644 --- a/linkmind/src/main/java/com/app/toaster/domain/Topic.java +++ b/linkmind/src/main/java/com/app/toaster/recommendsite/domain/Topic.java @@ -1,4 +1,4 @@ -package com.app.toaster.domain; +package com.app.toaster.recommendsite.domain; import lombok.AllArgsConstructor; import lombok.Getter; diff --git a/linkmind/src/main/java/com/app/toaster/infrastructure/RecommedSiteRepository.java b/linkmind/src/main/java/com/app/toaster/recommendsite/infrastructure/RecommedSiteRepository.java similarity index 52% rename from linkmind/src/main/java/com/app/toaster/infrastructure/RecommedSiteRepository.java rename to linkmind/src/main/java/com/app/toaster/recommendsite/infrastructure/RecommedSiteRepository.java index 425ce2e3..fb2fbcee 100644 --- a/linkmind/src/main/java/com/app/toaster/infrastructure/RecommedSiteRepository.java +++ b/linkmind/src/main/java/com/app/toaster/recommendsite/infrastructure/RecommedSiteRepository.java @@ -1,12 +1,9 @@ -package com.app.toaster.infrastructure; +package com.app.toaster.recommendsite.infrastructure; -import com.app.toaster.domain.RecommendSite; +import com.app.toaster.recommendsite.domain.RecommendSite; import org.springframework.data.jpa.repository.JpaRepository; -import java.util.ArrayList; -import java.util.List; - public interface RecommedSiteRepository extends JpaRepository { } diff --git a/linkmind/src/main/java/com/app/toaster/service/recommendSite/RecommendSiteService.java b/linkmind/src/main/java/com/app/toaster/recommendsite/service/RecommendSiteService.java similarity index 60% rename from linkmind/src/main/java/com/app/toaster/service/recommendSite/RecommendSiteService.java rename to linkmind/src/main/java/com/app/toaster/recommendsite/service/RecommendSiteService.java index 8da86813..0fc66795 100644 --- a/linkmind/src/main/java/com/app/toaster/service/recommendSite/RecommendSiteService.java +++ b/linkmind/src/main/java/com/app/toaster/recommendsite/service/RecommendSiteService.java @@ -1,12 +1,10 @@ -package com.app.toaster.service.recommendSite; +package com.app.toaster.recommendsite.service; -import com.app.toaster.domain.RecommendSite; -import com.app.toaster.infrastructure.RecommedSiteRepository; -import com.app.toaster.infrastructure.UserRepository; +import com.app.toaster.recommendsite.domain.RecommendSite; +import com.app.toaster.recommendsite.infrastructure.RecommedSiteRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; import java.util.List; diff --git a/linkmind/src/main/java/com/app/toaster/controller/response/search/CategoryResult.java b/linkmind/src/main/java/com/app/toaster/search/controller/response/CategoryResult.java similarity index 80% rename from linkmind/src/main/java/com/app/toaster/controller/response/search/CategoryResult.java rename to linkmind/src/main/java/com/app/toaster/search/controller/response/CategoryResult.java index 5e264c8e..8c7b98c7 100644 --- a/linkmind/src/main/java/com/app/toaster/controller/response/search/CategoryResult.java +++ b/linkmind/src/main/java/com/app/toaster/search/controller/response/CategoryResult.java @@ -1,4 +1,4 @@ -package com.app.toaster.controller.response.search; +package com.app.toaster.search.controller.response; public record CategoryResult(Long categoryId, String title, Long toastNum) { public static CategoryResult of(Long categoryId, String title, Long toastNum){ diff --git a/linkmind/src/main/java/com/app/toaster/controller/response/search/SearchCategoryResult.java b/linkmind/src/main/java/com/app/toaster/search/controller/response/SearchCategoryResult.java similarity index 70% rename from linkmind/src/main/java/com/app/toaster/controller/response/search/SearchCategoryResult.java rename to linkmind/src/main/java/com/app/toaster/search/controller/response/SearchCategoryResult.java index a1cb98ea..81e160a1 100644 --- a/linkmind/src/main/java/com/app/toaster/controller/response/search/SearchCategoryResult.java +++ b/linkmind/src/main/java/com/app/toaster/search/controller/response/SearchCategoryResult.java @@ -1,9 +1,7 @@ -package com.app.toaster.controller.response.search; +package com.app.toaster.search.controller.response; import java.util.List; -import com.app.toaster.domain.Category; - public record SearchCategoryResult(List categories) { public static SearchCategoryResult of(List categories){ return new SearchCategoryResult(categories); diff --git a/linkmind/src/main/java/com/app/toaster/controller/response/search/SearchMainResult.java b/linkmind/src/main/java/com/app/toaster/search/controller/response/SearchMainResult.java similarity index 73% rename from linkmind/src/main/java/com/app/toaster/controller/response/search/SearchMainResult.java rename to linkmind/src/main/java/com/app/toaster/search/controller/response/SearchMainResult.java index 70eb55f9..2889a364 100644 --- a/linkmind/src/main/java/com/app/toaster/controller/response/search/SearchMainResult.java +++ b/linkmind/src/main/java/com/app/toaster/search/controller/response/SearchMainResult.java @@ -1,8 +1,8 @@ -package com.app.toaster.controller.response.search; +package com.app.toaster.search.controller.response; import java.util.List; -import com.app.toaster.controller.response.toast.ToastDto; +import com.app.toaster.toast.controller.response.ToastDto; public record SearchMainResult(String keyword, List toasts, List categories) { public static SearchMainResult of(String keyword, List toasts, List categories){ diff --git a/linkmind/src/main/java/com/app/toaster/controller/response/search/ToastResult.java b/linkmind/src/main/java/com/app/toaster/search/controller/response/ToastResult.java similarity index 75% rename from linkmind/src/main/java/com/app/toaster/controller/response/search/ToastResult.java rename to linkmind/src/main/java/com/app/toaster/search/controller/response/ToastResult.java index b4416fd5..1f703074 100644 --- a/linkmind/src/main/java/com/app/toaster/controller/response/search/ToastResult.java +++ b/linkmind/src/main/java/com/app/toaster/search/controller/response/ToastResult.java @@ -1,4 +1,4 @@ -package com.app.toaster.controller.response.search; +package com.app.toaster.search.controller.response; public record ToastResult(Long toastId, String title) { public static ToastResult of(Long toastId, String title){ diff --git a/linkmind/src/main/java/com/app/toaster/service/search/SearchService.java b/linkmind/src/main/java/com/app/toaster/search/service/SearchService.java similarity index 70% rename from linkmind/src/main/java/com/app/toaster/service/search/SearchService.java rename to linkmind/src/main/java/com/app/toaster/search/service/SearchService.java index f1f78837..84c050e6 100644 --- a/linkmind/src/main/java/com/app/toaster/service/search/SearchService.java +++ b/linkmind/src/main/java/com/app/toaster/search/service/SearchService.java @@ -1,4 +1,4 @@ -package com.app.toaster.service.search; +package com.app.toaster.search.service; import java.util.List; import java.util.stream.Collectors; @@ -7,25 +7,20 @@ import org.springframework.transaction.annotation.Transactional; import com.app.toaster.common.dto.ApiResponse; -import com.app.toaster.controller.response.search.CategoryResult; -import com.app.toaster.controller.response.search.SearchCategoryResult; -import com.app.toaster.controller.response.search.SearchMainResult; -import com.app.toaster.controller.response.search.ToastResult; -import com.app.toaster.controller.response.toast.ToastDto; -import com.app.toaster.domain.Category; -import com.app.toaster.domain.Toast; -import com.app.toaster.domain.User; +import com.app.toaster.search.controller.response.CategoryResult; +import com.app.toaster.search.controller.response.SearchMainResult; +import com.app.toaster.toast.controller.response.ToastDto; +import com.app.toaster.category.domain.Category; +import com.app.toaster.toast.domain.Toast; +import com.app.toaster.user.domain.User; import com.app.toaster.exception.Error; import com.app.toaster.exception.Success; import com.app.toaster.exception.model.BadRequestException; -import com.app.toaster.exception.model.CustomException; import com.app.toaster.exception.model.NotFoundException; -import com.app.toaster.infrastructure.CategoryRepository; -import com.app.toaster.infrastructure.ToastRepository; -import com.app.toaster.infrastructure.UserRepository; -import com.google.protobuf.Api; +import com.app.toaster.category.infrastructure.CategoryRepository; +import com.app.toaster.toast.infrastructure.ToastRepository; +import com.app.toaster.user.infrastructure.UserRepository; -import jakarta.validation.constraints.NotNull; import lombok.RequiredArgsConstructor; @Service diff --git a/linkmind/src/main/java/com/app/toaster/service/category/CategoryQueryService.java b/linkmind/src/main/java/com/app/toaster/service/category/CategoryQueryService.java deleted file mode 100644 index bb7f9f87..00000000 --- a/linkmind/src/main/java/com/app/toaster/service/category/CategoryQueryService.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.app.toaster.service.category; - -import org.springframework.transaction.annotation.Transactional; - -import lombok.RequiredArgsConstructor; - -@Transactional(readOnly = true) -@RequiredArgsConstructor -public class CategoryQueryService { - - // R -} diff --git a/linkmind/src/main/java/com/app/toaster/service/category/CategoryUpdateService.java b/linkmind/src/main/java/com/app/toaster/service/category/CategoryUpdateService.java deleted file mode 100644 index 7b464601..00000000 --- a/linkmind/src/main/java/com/app/toaster/service/category/CategoryUpdateService.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.app.toaster.service.category; - -import org.springframework.transaction.annotation.Transactional; - -import lombok.RequiredArgsConstructor; - -@Transactional -@RequiredArgsConstructor -public class CategoryUpdateService { - - // CUD -} diff --git a/linkmind/src/main/java/com/app/toaster/controller/TimerController.java b/linkmind/src/main/java/com/app/toaster/timer/controller/TimerController.java similarity index 81% rename from linkmind/src/main/java/com/app/toaster/controller/TimerController.java rename to linkmind/src/main/java/com/app/toaster/timer/controller/TimerController.java index f8e6612d..dd516533 100644 --- a/linkmind/src/main/java/com/app/toaster/controller/TimerController.java +++ b/linkmind/src/main/java/com/app/toaster/timer/controller/TimerController.java @@ -1,24 +1,17 @@ -package com.app.toaster.controller; +package com.app.toaster.timer.controller; import com.app.toaster.common.dto.ApiResponse; -import com.app.toaster.config.UserId; -import com.app.toaster.controller.request.timer.CreateTimerRequestDto; -import com.app.toaster.controller.request.timer.UpdateTimerCommentDto; -import com.app.toaster.controller.request.timer.UpdateTimerDateTimeDto; -import com.app.toaster.controller.response.timer.GetTimerResponseDto; -import com.app.toaster.exception.Error; +import com.app.toaster.common.config.UserId; +import com.app.toaster.timer.controller.request.CreateTimerRequestDto; +import com.app.toaster.timer.controller.request.UpdateTimerCommentDto; +import com.app.toaster.timer.controller.request.UpdateTimerDateTimeDto; +import com.app.toaster.timer.controller.response.GetTimerResponseDto; import com.app.toaster.exception.Success; -import com.app.toaster.exception.model.CustomException; -import com.app.toaster.service.timer.TimerService; -import jakarta.validation.Valid; +import com.app.toaster.timer.service.TimerService; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.*; -import java.io.IOException; - @RestController @RequiredArgsConstructor @RequestMapping("/timer") diff --git a/linkmind/src/main/java/com/app/toaster/controller/request/timer/CreateTimerRequestDto.java b/linkmind/src/main/java/com/app/toaster/timer/controller/request/CreateTimerRequestDto.java similarity index 80% rename from linkmind/src/main/java/com/app/toaster/controller/request/timer/CreateTimerRequestDto.java rename to linkmind/src/main/java/com/app/toaster/timer/controller/request/CreateTimerRequestDto.java index 43ead24f..7180246d 100644 --- a/linkmind/src/main/java/com/app/toaster/controller/request/timer/CreateTimerRequestDto.java +++ b/linkmind/src/main/java/com/app/toaster/timer/controller/request/CreateTimerRequestDto.java @@ -1,4 +1,4 @@ -package com.app.toaster.controller.request.timer; +package com.app.toaster.timer.controller.request; import lombok.Getter; diff --git a/linkmind/src/main/java/com/app/toaster/controller/request/timer/UpdateTimerCommentDto.java b/linkmind/src/main/java/com/app/toaster/timer/controller/request/UpdateTimerCommentDto.java similarity index 54% rename from linkmind/src/main/java/com/app/toaster/controller/request/timer/UpdateTimerCommentDto.java rename to linkmind/src/main/java/com/app/toaster/timer/controller/request/UpdateTimerCommentDto.java index effa0185..b4ea5884 100644 --- a/linkmind/src/main/java/com/app/toaster/controller/request/timer/UpdateTimerCommentDto.java +++ b/linkmind/src/main/java/com/app/toaster/timer/controller/request/UpdateTimerCommentDto.java @@ -1,4 +1,4 @@ -package com.app.toaster.controller.request.timer; +package com.app.toaster.timer.controller.request; public record UpdateTimerCommentDto(String newComment) { } diff --git a/linkmind/src/main/java/com/app/toaster/controller/request/timer/UpdateTimerDateTimeDto.java b/linkmind/src/main/java/com/app/toaster/timer/controller/request/UpdateTimerDateTimeDto.java similarity index 70% rename from linkmind/src/main/java/com/app/toaster/controller/request/timer/UpdateTimerDateTimeDto.java rename to linkmind/src/main/java/com/app/toaster/timer/controller/request/UpdateTimerDateTimeDto.java index b09485ac..46c6369a 100644 --- a/linkmind/src/main/java/com/app/toaster/controller/request/timer/UpdateTimerDateTimeDto.java +++ b/linkmind/src/main/java/com/app/toaster/timer/controller/request/UpdateTimerDateTimeDto.java @@ -1,4 +1,4 @@ -package com.app.toaster.controller.request.timer; +package com.app.toaster.timer.controller.request; import java.util.ArrayList; diff --git a/linkmind/src/main/java/com/app/toaster/controller/response/timer/CompletedTimerDto.java b/linkmind/src/main/java/com/app/toaster/timer/controller/response/CompletedTimerDto.java similarity index 66% rename from linkmind/src/main/java/com/app/toaster/controller/response/timer/CompletedTimerDto.java rename to linkmind/src/main/java/com/app/toaster/timer/controller/response/CompletedTimerDto.java index 4d21a373..74228b43 100644 --- a/linkmind/src/main/java/com/app/toaster/controller/response/timer/CompletedTimerDto.java +++ b/linkmind/src/main/java/com/app/toaster/timer/controller/response/CompletedTimerDto.java @@ -1,10 +1,6 @@ -package com.app.toaster.controller.response.timer; +package com.app.toaster.timer.controller.response; -import com.app.toaster.controller.response.search.CategoryResult; -import com.app.toaster.controller.response.search.SearchCategoryResult; -import com.app.toaster.domain.Reminder; - -import java.util.List; +import com.app.toaster.timer.domain.Reminder; public record CompletedTimerDto(Long timerId, Long categoryId, String remindTime, String remindDate, String comment) { public static CompletedTimerDto of(Reminder timer,String remindTime, String remindDate){ diff --git a/linkmind/src/main/java/com/app/toaster/controller/response/timer/GetTimerPageResponseDto.java b/linkmind/src/main/java/com/app/toaster/timer/controller/response/GetTimerPageResponseDto.java similarity index 78% rename from linkmind/src/main/java/com/app/toaster/controller/response/timer/GetTimerPageResponseDto.java rename to linkmind/src/main/java/com/app/toaster/timer/controller/response/GetTimerPageResponseDto.java index cd47332e..72120552 100644 --- a/linkmind/src/main/java/com/app/toaster/controller/response/timer/GetTimerPageResponseDto.java +++ b/linkmind/src/main/java/com/app/toaster/timer/controller/response/GetTimerPageResponseDto.java @@ -1,4 +1,4 @@ -package com.app.toaster.controller.response.timer; +package com.app.toaster.timer.controller.response; import lombok.Builder; diff --git a/linkmind/src/main/java/com/app/toaster/controller/response/timer/GetTimerResponseDto.java b/linkmind/src/main/java/com/app/toaster/timer/controller/response/GetTimerResponseDto.java similarity index 65% rename from linkmind/src/main/java/com/app/toaster/controller/response/timer/GetTimerResponseDto.java rename to linkmind/src/main/java/com/app/toaster/timer/controller/response/GetTimerResponseDto.java index 9311b07b..ae436344 100644 --- a/linkmind/src/main/java/com/app/toaster/controller/response/timer/GetTimerResponseDto.java +++ b/linkmind/src/main/java/com/app/toaster/timer/controller/response/GetTimerResponseDto.java @@ -1,12 +1,8 @@ -package com.app.toaster.controller.response.timer; +package com.app.toaster.timer.controller.response; -import com.app.toaster.controller.response.search.CategoryResult; -import com.app.toaster.controller.response.search.SearchMainResult; -import com.app.toaster.controller.response.search.ToastResult; -import com.app.toaster.domain.Reminder; +import com.app.toaster.timer.domain.Reminder; import java.util.ArrayList; -import java.util.List; public record GetTimerResponseDto (String categoryName, String remindTime, diff --git a/linkmind/src/main/java/com/app/toaster/controller/response/timer/WaitingTimerDto.java b/linkmind/src/main/java/com/app/toaster/timer/controller/response/WaitingTimerDto.java similarity index 81% rename from linkmind/src/main/java/com/app/toaster/controller/response/timer/WaitingTimerDto.java rename to linkmind/src/main/java/com/app/toaster/timer/controller/response/WaitingTimerDto.java index 91860816..8e779bea 100644 --- a/linkmind/src/main/java/com/app/toaster/controller/response/timer/WaitingTimerDto.java +++ b/linkmind/src/main/java/com/app/toaster/timer/controller/response/WaitingTimerDto.java @@ -1,10 +1,8 @@ -package com.app.toaster.controller.response.timer; +package com.app.toaster.timer.controller.response; -import com.app.toaster.domain.Reminder; +import com.app.toaster.timer.domain.Reminder; import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.List; public record WaitingTimerDto(Long timerId, String remindTime, String remindDates, Boolean isAlarm, LocalDateTime updateAt, String comment, Long categoryId) { public static WaitingTimerDto of(Reminder timer, String remindTime, String remindDates) { diff --git a/linkmind/src/main/java/com/app/toaster/domain/Reminder.java b/linkmind/src/main/java/com/app/toaster/timer/domain/Reminder.java similarity index 86% rename from linkmind/src/main/java/com/app/toaster/domain/Reminder.java rename to linkmind/src/main/java/com/app/toaster/timer/domain/Reminder.java index 4f513176..e2a8802a 100644 --- a/linkmind/src/main/java/com/app/toaster/domain/Reminder.java +++ b/linkmind/src/main/java/com/app/toaster/timer/domain/Reminder.java @@ -1,19 +1,17 @@ -package com.app.toaster.domain; +package com.app.toaster.timer.domain; +import com.app.toaster.category.domain.Category; +import com.app.toaster.common.domain.IntegerListConverter; +import com.app.toaster.user.domain.User; import jakarta.persistence.*; import lombok.AccessLevel; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; -import org.jetbrains.annotations.NotNull; -import org.springframework.cglib.core.Local; -import org.springframework.format.annotation.DateTimeFormat; -import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.util.ArrayList; -import java.util.List; @Entity @Getter diff --git a/linkmind/src/main/java/com/app/toaster/infrastructure/TimerRepository.java b/linkmind/src/main/java/com/app/toaster/timer/infrastructure/TimerRepository.java similarity index 71% rename from linkmind/src/main/java/com/app/toaster/infrastructure/TimerRepository.java rename to linkmind/src/main/java/com/app/toaster/timer/infrastructure/TimerRepository.java index 53183b93..b9153d8d 100644 --- a/linkmind/src/main/java/com/app/toaster/infrastructure/TimerRepository.java +++ b/linkmind/src/main/java/com/app/toaster/timer/infrastructure/TimerRepository.java @@ -1,17 +1,15 @@ -package com.app.toaster.infrastructure; +package com.app.toaster.timer.infrastructure; -import com.app.toaster.domain.Category; -import com.app.toaster.domain.Reminder; -import com.app.toaster.domain.User; -import org.checkerframework.checker.units.qual.C; +import com.app.toaster.category.domain.Category; +import com.app.toaster.timer.domain.Reminder; +import com.app.toaster.user.domain.User; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; -import org.springframework.stereotype.Component; import org.springframework.stereotype.Repository; import java.util.ArrayList; -import java.util.List; + @Repository public interface TimerRepository extends JpaRepository { diff --git a/linkmind/src/main/java/com/app/toaster/service/timer/TimerService.java b/linkmind/src/main/java/com/app/toaster/timer/service/TimerService.java similarity index 91% rename from linkmind/src/main/java/com/app/toaster/service/timer/TimerService.java rename to linkmind/src/main/java/com/app/toaster/timer/service/TimerService.java index 0d063d6b..148e5f25 100644 --- a/linkmind/src/main/java/com/app/toaster/service/timer/TimerService.java +++ b/linkmind/src/main/java/com/app/toaster/timer/service/TimerService.java @@ -1,23 +1,23 @@ -package com.app.toaster.service.timer; - -import com.app.toaster.controller.request.timer.CreateTimerRequestDto; -import com.app.toaster.controller.request.timer.UpdateTimerCommentDto; -import com.app.toaster.controller.request.timer.UpdateTimerDateTimeDto; -import com.app.toaster.controller.response.timer.CompletedTimerDto; -import com.app.toaster.controller.response.timer.GetTimerPageResponseDto; -import com.app.toaster.controller.response.timer.GetTimerResponseDto; -import com.app.toaster.controller.response.timer.WaitingTimerDto; -import com.app.toaster.domain.Category; -import com.app.toaster.domain.Reminder; -import com.app.toaster.domain.User; +package com.app.toaster.timer.service; + +import com.app.toaster.timer.controller.request.CreateTimerRequestDto; +import com.app.toaster.timer.controller.request.UpdateTimerCommentDto; +import com.app.toaster.timer.controller.request.UpdateTimerDateTimeDto; +import com.app.toaster.timer.controller.response.CompletedTimerDto; +import com.app.toaster.timer.controller.response.GetTimerPageResponseDto; +import com.app.toaster.timer.controller.response.GetTimerResponseDto; +import com.app.toaster.timer.controller.response.WaitingTimerDto; +import com.app.toaster.category.domain.Category; +import com.app.toaster.timer.domain.Reminder; +import com.app.toaster.user.domain.User; import com.app.toaster.exception.Error; import com.app.toaster.exception.model.CustomException; import com.app.toaster.exception.model.ForbiddenException; import com.app.toaster.exception.model.NotFoundException; import com.app.toaster.exception.model.UnauthorizedException; -import com.app.toaster.infrastructure.CategoryRepository; -import com.app.toaster.infrastructure.TimerRepository; -import com.app.toaster.infrastructure.UserRepository; +import com.app.toaster.category.infrastructure.CategoryRepository; +import com.app.toaster.timer.infrastructure.TimerRepository; +import com.app.toaster.user.infrastructure.UserRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; diff --git a/linkmind/src/main/java/com/app/toaster/controller/ToastController.java b/linkmind/src/main/java/com/app/toaster/toast/controller/ToastController.java similarity index 75% rename from linkmind/src/main/java/com/app/toaster/controller/ToastController.java rename to linkmind/src/main/java/com/app/toaster/toast/controller/ToastController.java index 4093d251..096b9265 100644 --- a/linkmind/src/main/java/com/app/toaster/controller/ToastController.java +++ b/linkmind/src/main/java/com/app/toaster/toast/controller/ToastController.java @@ -1,12 +1,11 @@ -package com.app.toaster.controller; +package com.app.toaster.toast.controller; import java.io.IOException; import java.util.List; -import com.app.toaster.controller.request.toast.UpdateToastDto; -import com.app.toaster.controller.response.toast.ModifiedTitle; -import com.app.toaster.controller.response.toast.WeekLinkDto; -import com.app.toaster.service.link.LinkService; +import com.app.toaster.link.service.LinkService; +import com.app.toaster.toast.controller.request.*; +import com.app.toaster.toast.controller.response.*; import org.springframework.http.HttpStatus; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.DeleteMapping; @@ -21,14 +20,10 @@ import com.app.toaster.common.dto.ApiResponse; // import com.app.toaster.config.UserId; -import com.app.toaster.config.UserId; -import com.app.toaster.controller.request.toast.IsReadDto; -import com.app.toaster.controller.request.toast.OgRequestDto; -import com.app.toaster.controller.request.toast.SaveToastDto; -import com.app.toaster.controller.response.toast.IsReadResponse; +import com.app.toaster.common.config.UserId; import com.app.toaster.exception.Success; -import com.app.toaster.service.parse.ParsingService; -import com.app.toaster.service.toast.ToastService; +import com.app.toaster.parse.service.ParsingService; +import com.app.toaster.toast.service.ToastService; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; @@ -44,6 +39,7 @@ public class ToastController { @PostMapping("/og") @ResponseStatus(HttpStatus.OK) + @Deprecated public ApiResponse getOgAdvanced( @RequestBody OgRequestDto ogRequestDto ) throws IOException { @@ -89,6 +85,14 @@ public ApiResponse> getWeekLinks( return ApiResponse.success(Success.GET_LINKS_SUCCESS, linkService.getWeekLinks()); } + @GetMapping("/recent-saved") + @ResponseStatus(HttpStatus.OK) + public ApiResponse> getRecentSavedLinks( + @UserId Long userId + ) { + return ApiResponse.success(Success.GET_RECENT_TOAST_SUCCESS, toastService.getToastTop3_savedRecently(userId)); + } + @PatchMapping("/title") @ResponseStatus(HttpStatus.OK) public ApiResponse modifyTitle( @@ -98,5 +102,13 @@ public ApiResponse modifyTitle( return ApiResponse.success(Success.UPDATE_TOAST_TITLE_SUCCESS, toastService.modifyTitle(userId,updateToastDto)); } + @PatchMapping("/category") + @ResponseStatus(HttpStatus.OK) + public ApiResponse modifyCategory( + @UserId Long userId, + @Valid @RequestBody MoveToastDto updateToastDto + ){ + return ApiResponse.success(Success.MOVE_CATEGORY_SUCCESS, toastService.modifyClip(userId,updateToastDto)); + } } diff --git a/linkmind/src/main/java/com/app/toaster/controller/request/toast/IsReadDto.java b/linkmind/src/main/java/com/app/toaster/toast/controller/request/IsReadDto.java similarity index 54% rename from linkmind/src/main/java/com/app/toaster/controller/request/toast/IsReadDto.java rename to linkmind/src/main/java/com/app/toaster/toast/controller/request/IsReadDto.java index 18297ad0..46729f05 100644 --- a/linkmind/src/main/java/com/app/toaster/controller/request/toast/IsReadDto.java +++ b/linkmind/src/main/java/com/app/toaster/toast/controller/request/IsReadDto.java @@ -1,4 +1,4 @@ -package com.app.toaster.controller.request.toast; +package com.app.toaster.toast.controller.request; public record IsReadDto(Long toastId, Boolean isRead) { } diff --git a/linkmind/src/main/java/com/app/toaster/toast/controller/request/MoveToastDto.java b/linkmind/src/main/java/com/app/toaster/toast/controller/request/MoveToastDto.java new file mode 100644 index 00000000..bb9c1ea3 --- /dev/null +++ b/linkmind/src/main/java/com/app/toaster/toast/controller/request/MoveToastDto.java @@ -0,0 +1,6 @@ +package com.app.toaster.toast.controller.request; + +import jakarta.validation.constraints.NotNull; + +public record MoveToastDto(@NotNull Long toastId, @NotNull Long categoryId) { +} diff --git a/linkmind/src/main/java/com/app/toaster/toast/controller/request/OgRequestDto.java b/linkmind/src/main/java/com/app/toaster/toast/controller/request/OgRequestDto.java new file mode 100644 index 00000000..b039ae9b --- /dev/null +++ b/linkmind/src/main/java/com/app/toaster/toast/controller/request/OgRequestDto.java @@ -0,0 +1,4 @@ +package com.app.toaster.toast.controller.request; + +public record OgRequestDto(String linkUrl) { +} diff --git a/linkmind/src/main/java/com/app/toaster/toast/controller/request/SaveToastDto.java b/linkmind/src/main/java/com/app/toaster/toast/controller/request/SaveToastDto.java new file mode 100644 index 00000000..96ebf28e --- /dev/null +++ b/linkmind/src/main/java/com/app/toaster/toast/controller/request/SaveToastDto.java @@ -0,0 +1,4 @@ +package com.app.toaster.toast.controller.request; + +public record SaveToastDto(String linkUrl, Long categoryId) { +} diff --git a/linkmind/src/main/java/com/app/toaster/toast/controller/request/UpdateToastDto.java b/linkmind/src/main/java/com/app/toaster/toast/controller/request/UpdateToastDto.java new file mode 100644 index 00000000..6a54a61f --- /dev/null +++ b/linkmind/src/main/java/com/app/toaster/toast/controller/request/UpdateToastDto.java @@ -0,0 +1,13 @@ +package com.app.toaster.toast.controller.request; + +import com.app.toaster.utils.valid.Severity; +import com.app.toaster.utils.valid.TitleValid; + +import com.app.toaster.utils.valid.marker.ToastValidationGroup; +import jakarta.validation.constraints.NotNull; + +public record UpdateToastDto(Long toastId, @TitleValid(payload = Severity.Error.class, groups = {ToastValidationGroup.class}) @NotNull String title) { + public static UpdateToastDto of(Long toastId, String title){ + return new UpdateToastDto(toastId,title); + } +} diff --git a/linkmind/src/main/java/com/app/toaster/controller/response/toast/IsReadResponse.java b/linkmind/src/main/java/com/app/toaster/toast/controller/response/IsReadResponse.java similarity index 73% rename from linkmind/src/main/java/com/app/toaster/controller/response/toast/IsReadResponse.java rename to linkmind/src/main/java/com/app/toaster/toast/controller/response/IsReadResponse.java index cabc9463..ed8508aa 100644 --- a/linkmind/src/main/java/com/app/toaster/controller/response/toast/IsReadResponse.java +++ b/linkmind/src/main/java/com/app/toaster/toast/controller/response/IsReadResponse.java @@ -1,4 +1,4 @@ -package com.app.toaster.controller.response.toast; +package com.app.toaster.toast.controller.response; public record IsReadResponse(Boolean isRead) { public static IsReadResponse of(Boolean isRead){ diff --git a/linkmind/src/main/java/com/app/toaster/toast/controller/response/ModifiedCategory.java b/linkmind/src/main/java/com/app/toaster/toast/controller/response/ModifiedCategory.java new file mode 100644 index 00000000..aef5065b --- /dev/null +++ b/linkmind/src/main/java/com/app/toaster/toast/controller/response/ModifiedCategory.java @@ -0,0 +1,7 @@ +package com.app.toaster.toast.controller.response; + +public record ModifiedCategory(Long categoryId) { + public static ModifiedCategory of(Long categoryId){ + return new ModifiedCategory(categoryId); + } +} diff --git a/linkmind/src/main/java/com/app/toaster/controller/response/toast/ModifiedTitle.java b/linkmind/src/main/java/com/app/toaster/toast/controller/response/ModifiedTitle.java similarity index 75% rename from linkmind/src/main/java/com/app/toaster/controller/response/toast/ModifiedTitle.java rename to linkmind/src/main/java/com/app/toaster/toast/controller/response/ModifiedTitle.java index 362d8810..39c00b40 100644 --- a/linkmind/src/main/java/com/app/toaster/controller/response/toast/ModifiedTitle.java +++ b/linkmind/src/main/java/com/app/toaster/toast/controller/response/ModifiedTitle.java @@ -1,4 +1,4 @@ -package com.app.toaster.controller.response.toast; +package com.app.toaster.toast.controller.response; public record ModifiedTitle(String updatedTitle) { public static ModifiedTitle of(String updatedTitle){ diff --git a/linkmind/src/main/java/com/app/toaster/controller/response/toast/ToastDto.java b/linkmind/src/main/java/com/app/toaster/toast/controller/response/ToastDto.java similarity index 70% rename from linkmind/src/main/java/com/app/toaster/controller/response/toast/ToastDto.java rename to linkmind/src/main/java/com/app/toaster/toast/controller/response/ToastDto.java index bc79188f..9e59c506 100644 --- a/linkmind/src/main/java/com/app/toaster/controller/response/toast/ToastDto.java +++ b/linkmind/src/main/java/com/app/toaster/toast/controller/response/ToastDto.java @@ -1,7 +1,6 @@ -package com.app.toaster.controller.response.toast; +package com.app.toaster.toast.controller.response; -import com.app.toaster.controller.response.category.GetCategoryResponseDto; -import com.app.toaster.domain.Toast; +import com.app.toaster.toast.domain.Toast; public record ToastDto (Long toastId, String toastTitle, String linkUrl, Boolean isRead, String categoryTitle, String thumbnailUrl){ public static ToastDto of(Toast toast){ diff --git a/linkmind/src/main/java/com/app/toaster/controller/response/toast/ToastFilter.java b/linkmind/src/main/java/com/app/toaster/toast/controller/response/ToastFilter.java similarity index 53% rename from linkmind/src/main/java/com/app/toaster/controller/response/toast/ToastFilter.java rename to linkmind/src/main/java/com/app/toaster/toast/controller/response/ToastFilter.java index 33a916dd..61676578 100644 --- a/linkmind/src/main/java/com/app/toaster/controller/response/toast/ToastFilter.java +++ b/linkmind/src/main/java/com/app/toaster/toast/controller/response/ToastFilter.java @@ -1,4 +1,4 @@ -package com.app.toaster.controller.response.toast; +package com.app.toaster.toast.controller.response; public enum ToastFilter { ALL, diff --git a/linkmind/src/main/java/com/app/toaster/controller/response/toast/WeekLinkDto.java b/linkmind/src/main/java/com/app/toaster/toast/controller/response/WeekLinkDto.java similarity index 73% rename from linkmind/src/main/java/com/app/toaster/controller/response/toast/WeekLinkDto.java rename to linkmind/src/main/java/com/app/toaster/toast/controller/response/WeekLinkDto.java index 37c1d5ca..d728e92b 100644 --- a/linkmind/src/main/java/com/app/toaster/controller/response/toast/WeekLinkDto.java +++ b/linkmind/src/main/java/com/app/toaster/toast/controller/response/WeekLinkDto.java @@ -1,6 +1,6 @@ -package com.app.toaster.controller.response.toast; +package com.app.toaster.toast.controller.response; -import com.app.toaster.domain.Link; +import com.app.toaster.link.domain.Link; public record WeekLinkDto(Long linkId, String linkTitle, String linkImg, String linkUrl) { public static WeekLinkDto of(Link link){ diff --git a/linkmind/src/main/java/com/app/toaster/domain/Toast.java b/linkmind/src/main/java/com/app/toaster/toast/domain/Toast.java similarity index 85% rename from linkmind/src/main/java/com/app/toaster/domain/Toast.java rename to linkmind/src/main/java/com/app/toaster/toast/domain/Toast.java index d8ab1323..07918e50 100644 --- a/linkmind/src/main/java/com/app/toaster/domain/Toast.java +++ b/linkmind/src/main/java/com/app/toaster/toast/domain/Toast.java @@ -1,23 +1,23 @@ -package com.app.toaster.domain; +package com.app.toaster.toast.domain; import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.List; +import com.app.toaster.category.domain.Category; +import com.app.toaster.user.domain.User; import jakarta.persistence.*; import lombok.AccessLevel; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; -import lombok.Setter; @Getter @Entity @NoArgsConstructor(access = AccessLevel.PROTECTED) public class Toast { + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long Id; + private Long id; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id") @@ -70,4 +70,8 @@ public void setUpdateAt(){ this.updateAt=LocalDateTime.now(); } + public boolean isToastOwner(User presentUser){ + return this.user.equals(presentUser); + } + } diff --git a/linkmind/src/main/java/com/app/toaster/infrastructure/ToastRepository.java b/linkmind/src/main/java/com/app/toaster/toast/infrastructure/ToastRepository.java similarity index 88% rename from linkmind/src/main/java/com/app/toaster/infrastructure/ToastRepository.java rename to linkmind/src/main/java/com/app/toaster/toast/infrastructure/ToastRepository.java index 0b9c4f23..6be78d39 100644 --- a/linkmind/src/main/java/com/app/toaster/infrastructure/ToastRepository.java +++ b/linkmind/src/main/java/com/app/toaster/toast/infrastructure/ToastRepository.java @@ -1,17 +1,14 @@ -package com.app.toaster.infrastructure; +package com.app.toaster.toast.infrastructure; -import com.app.toaster.domain.Category; -import com.app.toaster.domain.User; +import com.app.toaster.category.domain.Category; +import com.app.toaster.user.domain.User; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; -import com.app.toaster.domain.Category; -import com.app.toaster.domain.Toast; -import com.app.toaster.domain.User; +import com.app.toaster.toast.domain.Toast; import org.springframework.data.jpa.repository.Modifying; -import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import java.time.LocalDateTime; @@ -64,4 +61,6 @@ Long countAllByUpdateAtThisWeek(@Param("startOfWeek") LocalDateTime startOfWeek, @Param("endOfWeek") LocalDateTime endOfWeek, @Param("user") User user); + List findTop3ByUserOrderByCreatedAtDesc(User user); + } diff --git a/linkmind/src/main/java/com/app/toaster/service/toast/ToastService.java b/linkmind/src/main/java/com/app/toaster/toast/service/ToastService.java similarity index 58% rename from linkmind/src/main/java/com/app/toaster/service/toast/ToastService.java rename to linkmind/src/main/java/com/app/toaster/toast/service/ToastService.java index c956ce46..646d55b5 100644 --- a/linkmind/src/main/java/com/app/toaster/service/toast/ToastService.java +++ b/linkmind/src/main/java/com/app/toaster/toast/service/ToastService.java @@ -1,38 +1,37 @@ -package com.app.toaster.service.toast; +package com.app.toaster.toast.service; import java.io.IOException; -import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; +import com.app.toaster.toast.controller.request.MoveToastDto; +import com.app.toaster.toast.controller.response.ModifiedCategory; +import com.app.toaster.toast.controller.response.ToastDto; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import com.app.toaster.controller.request.toast.IsReadDto; -import com.app.toaster.controller.request.toast.SaveToastDto; -import com.app.toaster.controller.request.toast.UpdateToastDto; -import com.app.toaster.controller.response.parse.OgResponse; -import com.app.toaster.controller.response.toast.IsReadResponse; -import com.app.toaster.controller.response.toast.ModifiedTitle; -import com.app.toaster.domain.Category; -import com.app.toaster.domain.Toast; -import com.app.toaster.domain.User; +import com.app.toaster.toast.controller.request.IsReadDto; +import com.app.toaster.toast.controller.request.SaveToastDto; +import com.app.toaster.toast.controller.request.UpdateToastDto; +import com.app.toaster.parse.controller.response.OgResponse; +import com.app.toaster.toast.controller.response.IsReadResponse; +import com.app.toaster.toast.controller.response.ModifiedTitle; +import com.app.toaster.category.domain.Category; +import com.app.toaster.toast.domain.Toast; +import com.app.toaster.user.domain.User; import com.app.toaster.exception.Error; import com.app.toaster.exception.model.BadRequestException; import com.app.toaster.exception.model.CustomException; import com.app.toaster.exception.model.ForbiddenException; import com.app.toaster.exception.model.NotFoundException; -import com.app.toaster.external.client.aws.ImagePresignedUrlResponse; -import com.app.toaster.external.client.aws.PresignedUrlVO; import com.app.toaster.external.client.aws.S3Service; -import com.app.toaster.infrastructure.CategoryRepository; -import com.app.toaster.infrastructure.ToastRepository; -import com.app.toaster.infrastructure.UserRepository; -import com.app.toaster.service.parse.ParsingService; +import com.app.toaster.category.infrastructure.CategoryRepository; +import com.app.toaster.toast.infrastructure.ToastRepository; +import com.app.toaster.user.infrastructure.UserRepository; +import com.app.toaster.parse.service.ParsingService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import software.amazon.awssdk.utils.Logger; @Service @RequiredArgsConstructor @@ -43,8 +42,6 @@ public class ToastService { private final CategoryRepository categoryRepository; private final S3Service s3Service; private final ParsingService parsingService; - private static final String TOAST_IMAGE_FOLDER_NAME = "toast/"; - private static Logger logger; @Value("${static-image.root}") private String BASIC_ROOT; @@ -53,9 +50,7 @@ public class ToastService { @Transactional public void createToast(Long userId, SaveToastDto saveToastDto){ //해당 유저 탐색 - User presentUser = userRepository.findByUserId(userId).orElseThrow( - ()-> new NotFoundException(Error.NOT_FOUND_USER_EXCEPTION, Error.NOT_FOUND_USER_EXCEPTION.getMessage()) - ); + User presentUser = findUser(userId); //토스트 생성 try { System.out.println(saveToastDto.linkUrl()); @@ -85,15 +80,8 @@ public void createToast(Long userId, SaveToastDto saveToastDto){ } @Transactional public IsReadResponse readToast(Long userId, IsReadDto isReadDto){ - User presentUser = userRepository.findByUserId(userId).orElseThrow( - ()-> new NotFoundException(Error.NOT_FOUND_USER_EXCEPTION, Error.NOT_FOUND_USER_EXCEPTION.getMessage()) - ); - Toast toast = toastRepository.findById(isReadDto.toastId()).orElseThrow( - () -> new NotFoundException(Error.NOT_FOUND_TOAST_EXCEPTION, Error.NOT_FOUND_TOAST_EXCEPTION.getMessage()) - ); - if (!presentUser.equals(toast.getUser())){ - throw new ForbiddenException(Error.INVALID_USER_ACCESS, Error.INVALID_USER_ACCESS.getMessage()); - } + Toast toast = findToast(isReadDto.toastId()); + isOwnerOfToast(userId,toast); if (isReadDto.isRead() && toast.getIsRead() || !isReadDto.isRead() && !toast.getIsRead()){ throw new BadRequestException(Error.BAD_REQUEST_ISREAD, Error.BAD_REQUEST_ISREAD.getMessage()); } @@ -106,8 +94,17 @@ public IsReadResponse readToast(Long userId, IsReadDto isReadDto){ return IsReadResponse.of(isReadDto.isRead()); } + @Transactional(readOnly = true) + public List getToastTop3_savedRecently(Long userId){ + User presentUser = findUser(userId); + return toastRepository.findTop3ByUserOrderByCreatedAtDesc(presentUser) + .stream() + .map(ToastDto::of) + .toList(); + } + @Transactional - public void deleteToast(Long userId, Long toastId) throws IOException { + public void deleteToast(Long userId, Long toastId) { User presentUser = userRepository.findByUserId(userId).orElseThrow( ()-> new NotFoundException(Error.NOT_FOUND_USER_EXCEPTION, Error.NOT_FOUND_USER_EXCEPTION.getMessage()) ); @@ -137,18 +134,44 @@ public void deleteAllToast(User user) throws IOException { @Transactional public ModifiedTitle modifyTitle(Long userId, UpdateToastDto updateToastDto){ - User presentUser = userRepository.findByUserId(userId).orElseThrow( - ()-> new NotFoundException(Error.NOT_FOUND_USER_EXCEPTION, Error.NOT_FOUND_USER_EXCEPTION.getMessage()) - ); - Toast toast = toastRepository.findById(updateToastDto.toastId()).orElseThrow( - () -> new BadRequestException(Error.BAD_REQUEST_ID, Error.BAD_REQUEST_ID.getMessage()) - ); - if (!presentUser.equals(toast.getUser())){ - throw new ForbiddenException(Error.UNAUTHORIZED_ACCESS, Error.UNAUTHORIZED_ACCESS.getMessage()); - } + Toast toast = findToast(updateToastDto.toastId()); + isOwnerOfToast(userId,toast); toast.updateTitle(updateToastDto.title()); return ModifiedTitle.of(updateToastDto.title()); + } + @Transactional + public ModifiedCategory modifyClip(Long userId, MoveToastDto updateToastDto){ + Toast toast = findToast(updateToastDto.toastId()); + isOwnerOfToast(userId, toast); + Category category = findCategory(updateToastDto.categoryId()); + toast.updateCategory(category); + return ModifiedCategory.of(updateToastDto.categoryId()); + } + + private void isOwnerOfToast(Long userId, Toast toast){ + User presentUser = findUser(userId); + if (!toast.isToastOwner(presentUser)){ + throw new ForbiddenException(Error.UNAUTHORIZED_ACCESS, Error.UNAUTHORIZED_ACCESS.getMessage()); + } + } + + private User findUser(Long userId){ + return userRepository.findByUserId(userId).orElseThrow( + ()-> new NotFoundException(Error.NOT_FOUND_USER_EXCEPTION, Error.NOT_FOUND_USER_EXCEPTION.getMessage()) + ); + } + + private Toast findToast(Long toastId){ + return toastRepository.findById(toastId).orElseThrow( + () -> new BadRequestException(Error.BAD_REQUEST_ID, Error.BAD_REQUEST_ID.getMessage()) + ); + } + + private Category findCategory(Long categoryId){ + return categoryRepository.findById(categoryId).orElseThrow( + () -> new NotFoundException(Error.NOT_FOUND_CATEGORY_EXCEPTION, Error.NOT_FOUND_CATEGORY_EXCEPTION.getMessage()) + ); } @@ -163,42 +186,6 @@ private void checkCategoryIsEmpty(Toast toast, Long categoryId){ toast.updateCategory(foundCategory); } } - // presigned url로 저장하는 로직 - private void convertToBytes(){ - // RestClient restClient = RestClient.create(BASIC_ROOT+res.imageAdvanced()); - // Response response = restClient.get(); - // byte[] imageBytes = response.content().asByteArray(); - } - - public ImagePresignedUrlResponse getUploadPreSignedUrl(String filename) { - try { - - PresignedUrlVO presignedUrlVO = s3Service.getUploadPreSignedUrl(filename ,TOAST_IMAGE_FOLDER_NAME); - return new ImagePresignedUrlResponse( - presignedUrlVO.fileName(), - presignedUrlVO.url() - ); - } catch (Exception e) { - - System.out.println(e.getMessage()); - System.out.println(String.valueOf(e.getCause())); - System.out.println(Arrays.toString(e.getStackTrace())); - throw e; - } - } - // presign url로 요청 보내는 api - private void requestPreSignedUrl(){ - // HttpHeaders headers = new HttpHeaders(); - // headers.setContentType(MediaType.APPLICATION_OCTET_STREAM); - // final String imageUrl = TOAST_IMAGE_FOLDER_NAME+realRes.fileName(); - // s3Service.getURL(imageUrl); - // - // HttpEntity entity = new HttpEntity<>(bytes, headers); - // - // // RestTemplate을 사용하여 PUT 요청을 보낸다. - // RestTemplate restTemplate = new RestTemplate(); - // restTemplate.put(realRes.preSignedUrl(), entity); - } private String checkIsBasicImage(String imageUrl){ if (!imageUrl.startsWith("http")){ diff --git a/linkmind/src/main/java/com/app/toaster/controller/UserController.java b/linkmind/src/main/java/com/app/toaster/user/controller/UserController.java similarity index 85% rename from linkmind/src/main/java/com/app/toaster/controller/UserController.java rename to linkmind/src/main/java/com/app/toaster/user/controller/UserController.java index 4767ae16..1ff45542 100644 --- a/linkmind/src/main/java/com/app/toaster/controller/UserController.java +++ b/linkmind/src/main/java/com/app/toaster/user/controller/UserController.java @@ -1,23 +1,22 @@ -package com.app.toaster.controller; +package com.app.toaster.user.controller; import java.util.HashMap; import java.util.Map; -import com.app.toaster.controller.response.main.MainPageResponseDto; +import com.app.toaster.mainpage.controller.response.MainPageResponseDto; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; import com.app.toaster.common.dto.ApiResponse; -import com.app.toaster.config.UserId; -import com.app.toaster.controller.request.user.UpdateAllowedPush; +import com.app.toaster.common.config.UserId; +import com.app.toaster.user.controller.request.UpdateAllowedPush; import com.app.toaster.exception.Success; -import com.app.toaster.service.UserService; +import com.app.toaster.user.service.UserService; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; diff --git a/linkmind/src/main/java/com/app/toaster/controller/request/user/UpdateAllowedPush.java b/linkmind/src/main/java/com/app/toaster/user/controller/request/UpdateAllowedPush.java similarity index 54% rename from linkmind/src/main/java/com/app/toaster/controller/request/user/UpdateAllowedPush.java rename to linkmind/src/main/java/com/app/toaster/user/controller/request/UpdateAllowedPush.java index 052ebd75..83d24106 100644 --- a/linkmind/src/main/java/com/app/toaster/controller/request/user/UpdateAllowedPush.java +++ b/linkmind/src/main/java/com/app/toaster/user/controller/request/UpdateAllowedPush.java @@ -1,4 +1,4 @@ -package com.app.toaster.controller.request.user; +package com.app.toaster.user.controller.request; public record UpdateAllowedPush(Boolean allowedPush) { } diff --git a/linkmind/src/main/java/com/app/toaster/controller/response/user/MyPageResponse.java b/linkmind/src/main/java/com/app/toaster/user/controller/response/MyPageResponse.java similarity index 87% rename from linkmind/src/main/java/com/app/toaster/controller/response/user/MyPageResponse.java rename to linkmind/src/main/java/com/app/toaster/user/controller/response/MyPageResponse.java index 75a42668..0d3ae956 100644 --- a/linkmind/src/main/java/com/app/toaster/controller/response/user/MyPageResponse.java +++ b/linkmind/src/main/java/com/app/toaster/user/controller/response/MyPageResponse.java @@ -1,4 +1,4 @@ -package com.app.toaster.controller.response.user; +package com.app.toaster.user.controller.response; public record MyPageResponse(String nickname, String profile, Long allReadToast, Long thisWeekendRead, Long thisWeekendSaved ) { public static MyPageResponse of(String nickname, String profile, Long allReadToast, Long thisWeekendRead, Long thisWeekendSaved ){ diff --git a/linkmind/src/main/java/com/app/toaster/controller/response/user/SettingResponse.java b/linkmind/src/main/java/com/app/toaster/user/controller/response/SettingResponse.java similarity index 80% rename from linkmind/src/main/java/com/app/toaster/controller/response/user/SettingResponse.java rename to linkmind/src/main/java/com/app/toaster/user/controller/response/SettingResponse.java index 03bc862e..1f48d10c 100644 --- a/linkmind/src/main/java/com/app/toaster/controller/response/user/SettingResponse.java +++ b/linkmind/src/main/java/com/app/toaster/user/controller/response/SettingResponse.java @@ -1,4 +1,4 @@ -package com.app.toaster.controller.response.user; +package com.app.toaster.user.controller.response; public record SettingResponse(String nickname, Boolean fcmIsAllowed) { public static SettingResponse of(String nickname, Boolean fcmIsAllowed){ diff --git a/linkmind/src/main/java/com/app/toaster/domain/SocialType.java b/linkmind/src/main/java/com/app/toaster/user/domain/SocialType.java similarity index 77% rename from linkmind/src/main/java/com/app/toaster/domain/SocialType.java rename to linkmind/src/main/java/com/app/toaster/user/domain/SocialType.java index 9d5e83d1..3d63ff1b 100644 --- a/linkmind/src/main/java/com/app/toaster/domain/SocialType.java +++ b/linkmind/src/main/java/com/app/toaster/user/domain/SocialType.java @@ -1,4 +1,4 @@ -package com.app.toaster.domain; +package com.app.toaster.user.domain; import lombok.AllArgsConstructor; import lombok.Getter; diff --git a/linkmind/src/main/java/com/app/toaster/domain/User.java b/linkmind/src/main/java/com/app/toaster/user/domain/User.java similarity index 97% rename from linkmind/src/main/java/com/app/toaster/domain/User.java rename to linkmind/src/main/java/com/app/toaster/user/domain/User.java index 4dfcaf95..a09dec12 100644 --- a/linkmind/src/main/java/com/app/toaster/domain/User.java +++ b/linkmind/src/main/java/com/app/toaster/user/domain/User.java @@ -1,4 +1,4 @@ -package com.app.toaster.domain; +package com.app.toaster.user.domain; import java.util.Objects; diff --git a/linkmind/src/main/java/com/app/toaster/infrastructure/UserRepository.java b/linkmind/src/main/java/com/app/toaster/user/infrastructure/UserRepository.java similarity index 79% rename from linkmind/src/main/java/com/app/toaster/infrastructure/UserRepository.java rename to linkmind/src/main/java/com/app/toaster/user/infrastructure/UserRepository.java index 6c240e9e..e8091f5a 100644 --- a/linkmind/src/main/java/com/app/toaster/infrastructure/UserRepository.java +++ b/linkmind/src/main/java/com/app/toaster/user/infrastructure/UserRepository.java @@ -1,11 +1,11 @@ -package com.app.toaster.infrastructure; +package com.app.toaster.user.infrastructure; import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; -import com.app.toaster.domain.SocialType; -import com.app.toaster.domain.User; +import com.app.toaster.user.domain.SocialType; +import com.app.toaster.user.domain.User; public interface UserRepository extends JpaRepository { Boolean existsBySocialIdAndSocialType(String socialId, SocialType socialType); diff --git a/linkmind/src/main/java/com/app/toaster/service/UserService.java b/linkmind/src/main/java/com/app/toaster/user/service/UserService.java similarity index 84% rename from linkmind/src/main/java/com/app/toaster/service/UserService.java rename to linkmind/src/main/java/com/app/toaster/user/service/UserService.java index ca95cf22..8cbb787f 100644 --- a/linkmind/src/main/java/com/app/toaster/service/UserService.java +++ b/linkmind/src/main/java/com/app/toaster/user/service/UserService.java @@ -1,21 +1,23 @@ -package com.app.toaster.service; +package com.app.toaster.user.service; -import com.app.toaster.controller.response.category.CategoryResponse; -import com.app.toaster.controller.response.main.MainPageResponseDto; -import com.app.toaster.domain.Category; -import com.app.toaster.infrastructure.CategoryRepository; +import com.app.toaster.category.controller.response.CategoryResponse; +import com.app.toaster.mainpage.controller.response.MainPageResponseDto; +import com.app.toaster.category.domain.Category; +import com.app.toaster.category.infrastructure.CategoryRepository; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import com.app.toaster.controller.response.user.MyPageResponse; -import com.app.toaster.controller.response.user.SettingResponse; -import com.app.toaster.domain.User; +import com.app.toaster.user.controller.response.MyPageResponse; +import com.app.toaster.user.controller.response.SettingResponse; +import com.app.toaster.user.domain.User; import com.app.toaster.exception.Error; import com.app.toaster.exception.model.BadRequestException; import com.app.toaster.exception.model.NotFoundException; -import com.app.toaster.infrastructure.ToastRepository; -import com.app.toaster.infrastructure.UserRepository; +import com.app.toaster.toast.infrastructure.ToastRepository; +import com.app.toaster.user.infrastructure.UserRepository; + +// import com.app.toaster.infrastructure.querydsl.CustomToastRepository; import lombok.RequiredArgsConstructor; @@ -31,6 +33,8 @@ public class UserService { private final UserRepository userRepository; private final ToastRepository toastRepository; + + // private final CustomToastRepository customToastRepository; private final CategoryRepository categoryRepository; @Transactional(readOnly = true) @@ -87,6 +91,7 @@ public MainPageResponseDto getMainPage(Long userId){ .categoryId(category.getCategoryId()) .categoryTitle(category.getTitle()) .toastNum(toastRepository.getAllByCategory(category).size()).build() + ).collect(Collectors.toList())).build(); return mainPageResponseDto; diff --git a/linkmind/src/main/java/com/app/toaster/controller/HealthCheckController.java b/linkmind/src/main/java/com/app/toaster/utils/health/HealthCheckController.java similarity index 52% rename from linkmind/src/main/java/com/app/toaster/controller/HealthCheckController.java rename to linkmind/src/main/java/com/app/toaster/utils/health/HealthCheckController.java index 3582b1f8..aad8785c 100644 --- a/linkmind/src/main/java/com/app/toaster/controller/HealthCheckController.java +++ b/linkmind/src/main/java/com/app/toaster/utils/health/HealthCheckController.java @@ -1,10 +1,16 @@ -package com.app.toaster.controller; +package com.app.toaster.utils.health; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; +import com.app.toaster.external.client.discord.DiscordMessageProvider; + +import lombok.RequiredArgsConstructor; + @RestController +@RequiredArgsConstructor public class HealthCheckController { + private final DiscordMessageProvider discordMessageProvider; @GetMapping("/health") public String healthCheck() { diff --git a/linkmind/src/main/java/com/app/toaster/controller/valid/Severity.java b/linkmind/src/main/java/com/app/toaster/utils/valid/Severity.java similarity index 79% rename from linkmind/src/main/java/com/app/toaster/controller/valid/Severity.java rename to linkmind/src/main/java/com/app/toaster/utils/valid/Severity.java index 66bc5868..6f99ec2e 100644 --- a/linkmind/src/main/java/com/app/toaster/controller/valid/Severity.java +++ b/linkmind/src/main/java/com/app/toaster/utils/valid/Severity.java @@ -1,4 +1,4 @@ -package com.app.toaster.controller.valid; +package com.app.toaster.utils.valid; import jakarta.validation.Payload; diff --git a/linkmind/src/main/java/com/app/toaster/controller/valid/TitleValid.java b/linkmind/src/main/java/com/app/toaster/utils/valid/TitleValid.java similarity index 82% rename from linkmind/src/main/java/com/app/toaster/controller/valid/TitleValid.java rename to linkmind/src/main/java/com/app/toaster/utils/valid/TitleValid.java index 43b9ad25..811f6a8e 100644 --- a/linkmind/src/main/java/com/app/toaster/controller/valid/TitleValid.java +++ b/linkmind/src/main/java/com/app/toaster/utils/valid/TitleValid.java @@ -1,10 +1,9 @@ -package com.app.toaster.controller.valid; +package com.app.toaster.utils.valid; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.Target; -import java.util.ArrayList; import jakarta.validation.Constraint; import jakarta.validation.Payload; @@ -17,5 +16,5 @@ String message() default "Invalid title"; Class[] groups() default {}; Class[] payload() default {}; - String pattern() default "[가-힣|a-z|A-Z|0-9|]"; + String pattern() default "^[\\S][가-힣ㄱ-ㅎㅏ-ㅣa-zA-Z0-9\\s]{0,20}$"; } diff --git a/linkmind/src/main/java/com/app/toaster/controller/valid/TitleValidator.java b/linkmind/src/main/java/com/app/toaster/utils/valid/TitleValidator.java similarity index 73% rename from linkmind/src/main/java/com/app/toaster/controller/valid/TitleValidator.java rename to linkmind/src/main/java/com/app/toaster/utils/valid/TitleValidator.java index ed537c23..5006f68a 100644 --- a/linkmind/src/main/java/com/app/toaster/controller/valid/TitleValidator.java +++ b/linkmind/src/main/java/com/app/toaster/utils/valid/TitleValidator.java @@ -1,18 +1,17 @@ -package com.app.toaster.controller.valid; - - -import com.app.toaster.exception.Error; -import com.app.toaster.exception.model.CustomException; +package com.app.toaster.utils.valid; +import com.app.toaster.utils.valid.marker.ToastValidationGroup; import jakarta.validation.ConstraintValidator; import jakarta.validation.ConstraintValidatorContext; public class TitleValidator implements ConstraintValidator { - public String pattern; + private String pattern; + private Class[] groups; @Override public void initialize(TitleValid constraintAnnotation) { this.pattern = constraintAnnotation.pattern(); + this.groups = constraintAnnotation.groups(); } @Override @@ -39,13 +38,13 @@ public boolean isValid(String title, ConstraintValidatorContext context) { return false; } - if(title.length()>15){ + if(!isGroupActive(ToastValidationGroup.class) && title.length()>15){ //토스트쪽이 아니면 검증을 합니다. context.buildConstraintViolationWithTemplate("이름은 최대 15자까지 입력 가능해요") .addConstraintViolation(); return false; } - if(!title.matches("^[\\S][가-힣ㄱ-ㅎㅏ-ㅣa-zA-Z0-9\\s]{0,20}$")){ + if(!title.matches(pattern)){ context.buildConstraintViolationWithTemplate("특수 문자로는 검색할 수 없어요.") .addConstraintViolation(); return false; @@ -53,4 +52,13 @@ public boolean isValid(String title, ConstraintValidatorContext context) { return true; } + + private boolean isGroupActive(Class targetGroup) { + for (Class group : groups) { + if (group.equals(targetGroup)) { + return true; + } + } + return false; + } } \ No newline at end of file diff --git a/linkmind/src/main/java/com/app/toaster/utils/valid/marker/ToastValidationGroup.java b/linkmind/src/main/java/com/app/toaster/utils/valid/marker/ToastValidationGroup.java new file mode 100644 index 00000000..46891c7c --- /dev/null +++ b/linkmind/src/main/java/com/app/toaster/utils/valid/marker/ToastValidationGroup.java @@ -0,0 +1,4 @@ +package com.app.toaster.utils.valid.marker; + +public interface ToastValidationGroup { +} diff --git a/linkmind/src/main/resources/data.sql b/linkmind/src/main/resources/data.sql deleted file mode 100644 index 8b137891..00000000 --- a/linkmind/src/main/resources/data.sql +++ /dev/null @@ -1 +0,0 @@ - diff --git a/linkmind/src/test/java/com/app/toaster/LinkmindApplicationTests.java b/linkmind/src/test/java/com/app/toaster/LinkmindApplicationTests.java deleted file mode 100644 index 43f0eec4..00000000 --- a/linkmind/src/test/java/com/app/toaster/LinkmindApplicationTests.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.app.toaster; - -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; - -@SpringBootTest -class LinkmindApplicationTests { - - @Test - void contextLoads() { - } - -} diff --git a/linkmind/src/test/java/com/app/toaster/base/BaseRepositoryTest.java b/linkmind/src/test/java/com/app/toaster/base/BaseRepositoryTest.java new file mode 100644 index 00000000..2edf55d3 --- /dev/null +++ b/linkmind/src/test/java/com/app/toaster/base/BaseRepositoryTest.java @@ -0,0 +1,15 @@ +package com.app.toaster.base; + +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.ActiveProfiles; + +// import com.app.toaster.config.TestJpaQueryFactoryConfig; + +// @Import(TestJpaQueryFactoryConfig.class) -> 쿼리dsl 사용 시 사용할 것. +@DataJpaTest //@Transactional 어노테이션을 포함하고 있다. 그래서 테스트가 완료되면 자동으로 롤백. +@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) +@ActiveProfiles("local") +public abstract class BaseRepositoryTest { +} diff --git a/linkmind/src/test/java/com/app/toaster/category/repository/CategoryRepositoryTest.java b/linkmind/src/test/java/com/app/toaster/category/repository/CategoryRepositoryTest.java new file mode 100644 index 00000000..48f0a3e1 --- /dev/null +++ b/linkmind/src/test/java/com/app/toaster/category/repository/CategoryRepositoryTest.java @@ -0,0 +1,38 @@ +package com.app.toaster.category.repository; + +import com.app.toaster.ToasterApplication; +import com.app.toaster.category.infrastructure.CategoryRepository; +import com.app.toaster.user.domain.User; +import com.app.toaster.user.infrastructure.UserRepository; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.test.context.ActiveProfiles; + +@DataJpaTest +@ActiveProfiles(profiles = "test") +@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) //mysql을 쓸 때는 이렇게 none으로 추가해줘야한다. +public class CategoryRepositoryTest { + + @Autowired + private CategoryRepository categoryRepository; + + @Autowired + private UserRepository userRepository; + + @Test + @DisplayName("category 우선순위 수정이 잘되는지 확인한다.") + void updateCategoryPriority(){ + categoryRepository.increasePriorityByOne(1L,1,3, 126L); + } + + @Test + @DisplayName("category를 우선순위별로 정렬하여 가져올때 리마인더를 가져오는지 확인한다.") + void getCategoryPriorityWithReminder(){ + User user = userRepository.findByUserId(126L).orElseThrow(); + + categoryRepository.findAllByUserOrderByPriority(user); + } +} diff --git a/linkmind/src/test/java/com/app/toaster/category/service/CategoryServiceTest.java b/linkmind/src/test/java/com/app/toaster/category/service/CategoryServiceTest.java new file mode 100644 index 00000000..44298ac1 --- /dev/null +++ b/linkmind/src/test/java/com/app/toaster/category/service/CategoryServiceTest.java @@ -0,0 +1,106 @@ +package com.app.toaster.category.service; + +import com.app.toaster.category.controller.request.ChangeCateoryPriorityDto; +import com.app.toaster.category.domain.Category; +import com.app.toaster.category.infrastructure.CategoryRepository; +import com.app.toaster.category.service.CategoryService; +import com.app.toaster.user.infrastructure.UserRepository; + +import org.junit.jupiter.api.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Random; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.stream.Collectors; + +@SpringBootTest +@ActiveProfiles(profiles = "test") +@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) //mysql을 쓸 때는 이렇게 none으로 추가해줘야한다. +//@Transactional +public class CategoryServiceTest { + + @Autowired + private CategoryService categoryService; + + @Autowired + private CategoryRepository categoryRepository; + + @Autowired + private UserRepository userRepository; + + private static final ArrayList list = new ArrayList<>(); + + + @BeforeEach + void preMakeList() { + Random random = new Random(); + list.add(new ChangeCateoryPriorityDto(random.nextLong(1, 5), random.nextInt(1, 5))); + list.add(new ChangeCateoryPriorityDto(random.nextLong(1, 5), random.nextInt(1, 5))); + } + + @AfterEach + void postList() { + list.clear(); + } + + + @Test + @DisplayName("category 우선순위 동시 수정을 무작위로 진행 했을 때 정합성이 유지된다.") + void categoryDeadLock() throws InterruptedException { + + ExecutorService executorService = Executors.newFixedThreadPool(2); + CountDownLatch latch = new CountDownLatch(2); + + executorService.execute(() -> { + try { + categoryService.editCategoryPriority(126L, list.get(0)); + } finally { + latch.countDown(); + } + }); //우선순위 순 -> 3 1 2 4 id순 -> 2,3,1,4 + executorService.execute(() -> { + try { + categoryService.editCategoryPriority(126L, list.get(1)); + } finally { + latch.countDown(); + } + }); //3,1,4,2 + latch.await(); + + ArrayList newList = categoryRepository.findAllByUserOrderByPriority(userRepository.findByUserId(126L).get()); + HashSet answer = newList.stream() + .map(Category::getPriority) + .collect(Collectors.toCollection(HashSet::new)); + + Assertions.assertEquals(4, answer.size()); //충돌 시 어떤 것이 바뀌었든, 같은 숫자의 우선순위가 들어가지 않는다. + } + + @Test + @DisplayName("category 우선순위 수정을 동시에 진행 하지않았을 때도 정합성이 유지된다.") + void categoryDeadLockValid() throws InterruptedException { + ArrayList list = new ArrayList<>(); + list.add(new ChangeCateoryPriorityDto(1L, 3)); + list.add(new ChangeCateoryPriorityDto(1L, 4)); + + categoryService.editCategoryPriority(126L, list.get(0)); + //2,3,1,4 + categoryService.editCategoryPriority(126L, list.get(1)); + //3,1,4,2 + ArrayList newList = categoryRepository.findAllByUserOrderByPriority(userRepository.findByUserId(126L).get()); + HashSet answer = newList.stream() + .map(Category::getPriority) + .collect(Collectors.toCollection(HashSet::new)); + + Assertions.assertEquals(4, answer.size()); //아 마지막꺼만 적용되나본데? + } + + +} diff --git a/linkmind/src/test/java/com/app/toaster/config/TestJpaQueryFactoryConfig.java b/linkmind/src/test/java/com/app/toaster/config/TestJpaQueryFactoryConfig.java new file mode 100644 index 00000000..6c14f110 --- /dev/null +++ b/linkmind/src/test/java/com/app/toaster/config/TestJpaQueryFactoryConfig.java @@ -0,0 +1,23 @@ +// package com.app.toaster.config; +// +// import org.springframework.boot.test.context.TestConfiguration; +// import org.springframework.context.annotation.Bean; +// import org.springframework.data.jpa.repository.config.EnableJpaAuditing; +// +// import com.querydsl.jpa.impl.JPAQueryFactory; +// +// import jakarta.persistence.EntityManager; +// import jakarta.persistence.PersistenceContext; +// +// @EnableJpaAuditing +// @TestConfiguration +// public class TestJpaQueryFactoryConfig { +// +// @PersistenceContext +// private EntityManager entityManager; +// +// @Bean +// JPAQueryFactory jpaQueryFactory(){ +// return new JPAQueryFactory(entityManager); +// } +// } diff --git a/linkmind/src/test/java/com/app/toaster/infrastructure/querydsl/CustomToastRepositoryTest.java b/linkmind/src/test/java/com/app/toaster/infrastructure/querydsl/CustomToastRepositoryTest.java new file mode 100644 index 00000000..7d9f06e5 --- /dev/null +++ b/linkmind/src/test/java/com/app/toaster/infrastructure/querydsl/CustomToastRepositoryTest.java @@ -0,0 +1,293 @@ +// package com.app.toaster.infrastructure.querydsl; +// +// import static com.app.toaster.domain.QToast.*; +// import static com.app.toaster.fixture.Fixture.*; +// import static org.junit.jupiter.api.Assertions.*; +// +// import java.util.ArrayList; +// import java.util.List; +// +// import org.assertj.core.api.Assertions; +// import org.junit.jupiter.api.AfterEach; +// import org.junit.jupiter.api.BeforeEach; +// import org.junit.jupiter.api.DisplayName; +// import org.junit.jupiter.api.Nested; +// import org.junit.jupiter.api.Test; +// import org.junit.jupiter.api.extension.ExtendWith; +// import org.springframework.beans.factory.annotation.Autowired; +// import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +// import org.springframework.context.annotation.Import; +// import org.springframework.test.context.junit.jupiter.SpringExtension; +// import org.springframework.transaction.annotation.Transactional; +// +// import com.app.toaster.config.JpaQueryFactoryConfig; +// import com.app.toaster.toast.domain.Toast; +// import com.app.toaster.fixture.Fixture; +// import com.app.toaster.category.infrastructure.CategoryRepository; +// import com.app.toaster.toast.infrastructure.ToastRepository; +// import com.app.toaster.user.infrastructure.UserRepository; +// import com.querydsl.core.types.dsl.BooleanExpression; +// import com.querydsl.jpa.impl.JPAQueryFactory; +// +// import jakarta.persistence.EntityManager; +// import lombok.extern.slf4j.Slf4j; +// +// @Slf4j +// @ExtendWith(SpringExtension.class) +// @DataJpaTest(showSql = true) +// @Import(JpaQueryFactoryConfig.class) +// class CustomToastRepositoryTest { +// +// CustomToastRepository customToastRepository; +// JPAQueryFactory jpaQueryFactory; +// +// @Autowired +// ToastRepository toastRepository; +// +// @Autowired +// UserRepository userRepository; +// +// @Autowired +// CategoryRepository categoryRepository; +// +// @Autowired +// EntityManager em; +// +// @BeforeEach +// void setup() { +// jpaQueryFactory = new JPAQueryFactory(em); +// customToastRepository = new CustomToastRepository(jpaQueryFactory); +// +// userRepository.save(USER_1); +// categoryRepository.save(CATEGORY_1); +// categoryRepository.save(CATEGORY_2); +// toastRepository.save(TOAST_1); +// toastRepository.save(TOAST_2); +// toastRepository.save(TOAST_3); +// } +// @AfterEach +// void tearDown() { +// // 테스트 후 정리 작업 수행 +// em.clear(); +// userRepository.deleteAll(); +// categoryRepository.deleteAll(); +// toastRepository.deleteAll(); +// } +// +// +// @Nested +// @DisplayName("토스트 jpa repository test") +// class 토스트_JpaRepository_Test { +// +// @Nested +// @DisplayName("토스트 카테고리 jpa repository test") +// class 토스트_카테고리_jpa_test{ +// @Test +// @DisplayName("토스트의 getAllByCategory test") +// void test_ToastJpaRepository_조회() { +// +// List toastList = toastRepository.getAllByCategory(CATEGORY_1); +// +// +// Assertions.assertThat(toastList.get(0).getId()).isEqualTo(Fixture.TOAST_1.getId()); +// Assertions.assertThat(toastList.get(1).getId()).isEqualTo(Fixture.TOAST_2.getId()); +// Assertions.assertThat(toastList.get(2).getId()).isEqualTo(Fixture.TOAST_3.getId()); +// } +// } +// @Nested +// @DisplayName("토스트 jpa repository 검색 test") +// class 토스트_JPA_검색_test{ +// +// @Test +// @DisplayName("토스트의 검색 쿼리 문자열이 앞에 있는 경우 test") +// void test_ToastJpaRepository_검색1(){ +// List toastList = toastRepository.searchToastsByQuery(USER_1.getUserId(),"검색"); +// +// Assertions.assertThat(toastList.get(0).getId()).isEqualTo(Fixture.TOAST_1.getId()); +// Assertions.assertThat(toastList.get(1).getId()).isEqualTo(Fixture.TOAST_2.getId()); +// Assertions.assertThat(toastList.get(2).getId()).isEqualTo(Fixture.TOAST_3.getId()); +// } +// +// @Test +// @DisplayName("토스트의 검색 쿼리 문자열이 뒤에 있는 경우 test") +// void test_ToastJpaRepository_검색2(){ +// List toastList = toastRepository.searchToastsByQuery(USER_1.getUserId(),"되나"); +// +// Assertions.assertThat(toastList.get(0).getId()).isEqualTo(TOAST_1.getId()); +// Assertions.assertThat(toastList.get(1).getId()).isEqualTo(TOAST_3.getId()); +// // Assertions.assertThat(toastList.get(2).getId()).isEqualTo(Fixture.TOAST_3.getId()); +// } +// +// } +// +// @DisplayName("토스트 수정 관련 jpa test") +// @Nested +// class 토스트_JPA_수정_TEST{ +// @Test +// @DisplayName("토스트 수정 쿼리 jpa test") +// @Transactional +// void updateCategoryIdsToNull(){ +// //given +// List categoryIds = new ArrayList<>(); +// categoryIds.add(CATEGORY_1.getCategoryId()); +// categoryIds.add(CATEGORY_2.getCategoryId()); +// +// //when +// toastRepository.updateCategoryIdsToNull(categoryIds); +// +// //then +// Assertions.assertThat(TOAST_1.getCategory() == null); +// Assertions.assertThat(TOAST_2.getCategory() == null); +// Assertions.assertThat(TOAST_3.getCategory() == null); +// Assertions.assertThat(TOAST_3_CATEGORY_2.getCategory() == null); +// } +// } +// } +// @Nested +// @DisplayName("토스트 querydsl test") +// class 토스트_QueryDSL_Test{ +// @Nested +// @DisplayName("토스트 QueryDSL category test") +// class 토스트_QueryDSL_Category_Test { +// +// @Test +// @DisplayName("토스트의 queryDSL getAllByCategory test") +// void ToastQueryRepository_getAllByCategory_테스트() { +// +// List toasts = customToastRepository.getAllByCategory(CATEGORY_1); +// +// Assertions.assertThat(toasts.get(0).getId()).isEqualTo(Fixture.TOAST_1.getId()); +// Assertions.assertThat(toasts.get(1).getId()).isEqualTo(Fixture.TOAST_2.getId()); +// Assertions.assertThat(toasts.get(2).getId()).isEqualTo(Fixture.TOAST_3.getId()); +// } +// +// @Test +// @DisplayName("categoryid 결과를 찾지 못했을 때 test") +// void ToastQueryRepository_Category_id_2_테스트() { +// +// List toasts = jpaQueryFactory.selectFrom(toast) +// .where(eqCategoryId(CATEGORY_2.getCategoryId())) +// .fetch(); +// assertNotNull(toasts, "조회 데이터가 없습니다."); +// // Assertions.assertThat(toasts.get(0).getId()).isEqualTo(Fixture.TOAST_2.getId()); +// // Assertions.assertThat(toasts.get(0).getId()).isEqualTo(Fixture.TOAST_3.getId()); +// } +// +// } +// +// @Nested +// @DisplayName("토스트 QueryDSL search test") +// class 토스트_QueryDSL_검색쿼리_test{ +// @Test +// @DisplayName("토스트의 queryDSL 문자열이 앞에 있을 때 쿼리 검색 test") +// @Transactional(readOnly = true) +// void ToastQueryRepository_searchToastsByQuery_테스트() { +// +// List toasts = customToastRepository.searchToastsByQuery(USER_1.getUserId(), "검색"); +// +// Assertions.assertThat(toasts.get(0).getId()).isEqualTo(Fixture.TOAST_1.getId()); +// Assertions.assertThat(toasts.get(1).getId()).isEqualTo(Fixture.TOAST_2.getId()); +// Assertions.assertThat(toasts.get(2).getId()).isEqualTo(Fixture.TOAST_3.getId()); +// } +// +// @Test +// @DisplayName("토스트의 queryDSL 문자열이 뒤에 있을 때 쿼리 검색 test") +// @Transactional(readOnly = true) +// void ToastQueryRepository_searchToastsByQuery_테스트2() { +// +// List toasts = customToastRepository.searchToastsByQuery(USER_1.getUserId(), "되나"); +// +// Assertions.assertThat(toasts.get(0).getId()).isEqualTo(Fixture.TOAST_1.getId()); +// Assertions.assertThat(toasts.get(1).getId()).isEqualTo(Fixture.TOAST_3.getId()); +// // Assertions.assertThat(toasts.get(2).getId()).isEqualTo(Fixture.TOAST_3.getId()); +// } +// +// @Test +// @DisplayName("검색 쿼리가 비어있을 때") +// @Transactional(readOnly = true) +// void ToastQueryRepository_searchToastsByQuery_테스트3() { +// +// List toasts = customToastRepository.searchToastsByQuery(USER_1.getUserId(), ""); +// +// Assertions.assertThat(toasts.get(0).getId()).isEqualTo(Fixture.TOAST_1.getId()); +// Assertions.assertThat(toasts.get(1).getId()).isEqualTo(Fixture.TOAST_2.getId()); +// Assertions.assertThat(toasts.get(2).getId()).isEqualTo(Fixture.TOAST_3.getId()); +// } +// } +// //적용하더라도 프로덕션 코드의 em부분 em.flush, em.clear체크. +// +// @Nested +// @DisplayName("토스트 수정 관련 querydsl test") +// class 토스트_QueryDSL_수정_TEST{ +// @Test +// @DisplayName("토스트 수정 쿼리 querydsl test") +// @Transactional +// void updateCategoryIdsToNull(){ +// //given +// List categoryIds = new ArrayList<>(); +// categoryIds.add(CATEGORY_1.getCategoryId()); +// categoryIds.add(CATEGORY_2.getCategoryId()); +// +// //when +// customToastRepository.updateCategoryIdsToNull(categoryIds); +// // +// em.flush(); +// em.clear(); +// +// //then +// Assertions.assertThat(TOAST_1.getCategory() == null); +// Assertions.assertThat(TOAST_2.getCategory() == null); +// Assertions.assertThat(TOAST_3.getCategory() == null); +// Assertions.assertThat(TOAST_3_CATEGORY_2.getCategory() == null); +// } +// } +// +// @Nested +// @DisplayName("토스트 QueryDSL 안읽은 토스트 개수 세기 test") +// class 토스트_QueryDSL_안읽은_토스트_개수_세기_test{ +// +// @Test +// @DisplayName("안읽은 토스트 개수 세기 쿼리 querydsl test") +// void countUnReadToast(){ +// //given +// TOAST_1.updateIsRead(false); +// TOAST_2.updateIsRead(false); +// em.flush(); +// +// //when +// Integer res = customToastRepository.getUnReadToastNumber(USER_1.getUserId()); +// // +// //em.clear(); +// +// //then +// Assertions.assertThat(res == 2); +// } +// +// @Test +// @DisplayName("안 읽은 토스트 개수가 없는 경우. querydsl test") +// void countUnReadToastAtNotFoundCase(){ +// //given +// TOAST_1.updateIsRead(true); +// TOAST_2.updateIsRead(true); +// TOAST_3.updateIsRead(true); +// em.flush(); +// +// //when +// Integer res = customToastRepository.getUnReadToastNumber(USER_1.getUserId()); +// // +// //em.clear(); +// +// //then +// Assertions.assertThat(res == 0); +// } +// +// } +// } +// +// +// +// private BooleanExpression eqCategoryId(Long id){ +// return id!=null?toast.category.categoryId.eq(id):null; +// } +// +// } \ No newline at end of file