From 2f15d29a024745b6f0f2552df8a28670f61b8c88 Mon Sep 17 00:00:00 2001 From: Hyeonae Date: Sun, 4 Jun 2023 18:35:50 +0900 Subject: [PATCH] Update server code --- CPR2U-Server/.gitignore | 2 +- CPR2U-Server/Dockerfile | 2 +- CPR2U-Server/README.md | 100 ++++- CPR2U-Server/build.gradle | 71 +++- CPR2U-Server/images/cpr2u_erd.png | Bin 0 -> 285119 bytes .../mentionall/cpr2u/Cpr2uApplication.java | 3 + .../call/controller/CprCallController.java | 18 +- .../call/controller/DispatchController.java | 6 +- .../mentionall/cpr2u/call/domain/CprCall.java | 10 +- .../cpr2u/call/domain/Dispatch.java | 2 +- .../mentionall/cpr2u/call/dto/FcmMessage.java | 32 -- .../CprCallGuideResponseDto.java | 4 +- .../CprCallIdResponseDto.java} | 4 +- .../CprCallNearUserResponseDto.java} | 10 +- .../CprCallRequestDto.java} | 8 +- .../CprCallResponseDto.java} | 16 +- .../{ => dispatch}/DispatchRequestDto.java | 2 +- .../{ => dispatch}/DispatchResponseDto.java | 14 +- .../call/repository/CprCallDslRepository.java | 4 +- .../repository/CprCallRepositoryImpl.java | 8 +- .../repository/DispatchDslRepository.java | 11 + .../call/repository/DispatchRepository.java | 6 +- .../repository/DispatchRepositoryImpl.java | 27 ++ .../call/repository/ReportRepository.java | 3 +- .../cpr2u/call/service/CprCallService.java | 142 ++++--- .../cpr2u/call/service/DispatchService.java | 15 +- .../service/FirebaseCloudMessageService.java | 70 ---- .../config/security/JwtTokenProvider.java | 14 +- .../config/security/WebSecurityConfig.java | 6 +- .../controller/EducationController.java | 33 +- .../education/domain/EducationProgress.java | 80 ---- .../cpr2u/education/domain/Lecture.java | 3 +- .../education/domain/ProgressStatus.java | 2 - .../cpr2u/education/domain/TestStandard.java | 11 +- .../domain/progress/EducationProgress.java | 68 ++++ .../domain/progress/LectureProgress.java | 37 ++ .../domain/progress/PostureProgress.java | 23 ++ .../domain/progress/QuizProgress.java | 23 ++ ...gressDto.java => ProgressResponseDto.java} | 38 +- .../{ScoreDto.java => ScoreRequestDto.java} | 2 +- .../LectureListResponseDto.java} | 10 +- .../lecture/PostureLectureResponseDto.java | 14 - .../EducationProgressRepository.java | 2 +- .../service/EducationProgressService.java | 56 ++- .../education/service/LectureService.java | 30 +- .../cpr2u/education/service/QuizService.java | 12 +- .../cpr2u/manager/ManagerController.java | 7 + .../cpr2u/manager/ManagerService.java | 64 ++-- .../cpr2u/user/controller/AuthController.java | 64 +++- .../cpr2u/user/controller/UserController.java | 16 +- ...{AngelStatusEnum.java => AngelStatus.java} | 2 +- .../cpr2u/user/domain/Certificate.java | 29 ++ .../cpr2u/user/domain/DeviceToken.java | 4 + .../cpr2u/user/domain/FcmToken.java | 4 +- .../mentionall/cpr2u/user/domain/User.java | 49 +-- .../cpr2u/user/dto/UserNicknameDto.java | 21 -- .../dto/{ => address}/AddressRequestDto.java | 2 +- .../dto/{ => address}/AddressResponseDto.java | 3 +- .../dto/{ => address}/SigugunResponseDto.java | 2 +- .../CodeResponseDto.java} | 4 +- .../LoginRequestDto.java} | 4 +- .../PhoneNumberRequestDto.java} | 4 +- .../SignUpRequestDto.java} | 8 +- .../TokenReissueRequestDto.java} | 4 +- .../TokenResponseDto.java} | 4 +- .../user/repository/AddressDslRepository.java | 9 - .../repository/DeviceTokenDslRepository.java | 9 - .../repository/DeviceTokenRepositoryImpl.java | 27 -- .../cpr2u/user/repository/UserRepository.java | 7 +- .../address/AddressDslRepository.java | 12 + .../{ => address}/AddressRepository.java | 9 +- .../{ => address}/AddressRepositoryImpl.java | 17 +- .../DeviceTokenDslRepository.java | 9 + .../DeviceTokenRepository.java | 2 +- .../DeviceTokenRepositoryImpl.java | 31 ++ .../cpr2u/user/service/AddressService.java | 40 +- .../cpr2u/user/service/AuthService.java | 135 +++++++ .../cpr2u/user/service/UserService.java | 111 +----- .../mentionall/cpr2u/util/CsvFileParser.java | 42 +++ .../cpr2u/util/exception/ResponseCode.java | 6 +- .../cpr2u/util/fcm/FcmDataType.java | 16 + .../{MessageEnum.java => fcm/FcmMessage.java} | 4 +- .../fcm/FcmPushType.java} | 4 +- .../util/fcm/FirebaseCloudMessageUtil.java | 82 +++++ .../cpr2u/util/twilio/FakeTwilioUtil.java | 15 + .../cpr2u/util/twilio/TwilioUtil.java | 32 ++ .../src/main/resources/application.properties | 8 + .../src/main/resources/application.yml | 24 -- .../main/resources/cpr2u_address_table.csv | 249 +++++++++++++ .../cpr2u/Cpr2uApplicationTests.java | 4 +- .../java/com/mentionall/cpr2u/TestConfig.java | 21 ++ .../repository/FakeCprCallRepository.java | 234 ------------ .../repository/FakeDispatchRepository.java | 178 --------- .../call/repository/FakeReportRepository.java | 178 --------- .../call/service/CprCallServiceTest.java | 280 +++++++------- .../call/service/DispatchServiceTest.java | 155 +++++--- .../util/FakeFirebaseCloudMessageUtil.java | 14 + .../FakeEducationProgressRepository.java | 181 --------- .../repository/FakeLectureRepository.java | 178 --------- .../repository/FakeQuizRepository.java | 223 ----------- .../service/EducationProgressTest.java | 345 +++++++++++------- .../education/service/LectureServiceTest.java | 87 +++-- .../education/service/QuizServiceTest.java | 77 ++-- .../manager/service/ManagerServiceTest.java | 77 ++++ .../repository/FakeAddressRepository.java | 260 ------------- .../repository/FakeDeviceTokenRepository.java | 222 ----------- .../user/repository/FakeUserRepository.java | 203 ----------- .../user/service/AddressServiceTest.java | 72 ++-- .../cpr2u/user/service/AuthServiceTest.java | 133 ++++--- .../cpr2u/user/service/UserServiceTest.java | 47 +++ .../src/test/resources/application.yml | 2 - .../src/test/resources/config/application.yml | 34 ++ 112 files changed, 2370 insertions(+), 3084 deletions(-) create mode 100644 CPR2U-Server/images/cpr2u_erd.png delete mode 100644 CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/dto/FcmMessage.java rename CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/dto/{ => cpr_call}/CprCallGuideResponseDto.java (82%) rename CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/dto/{CprCallIdDto.java => cpr_call/CprCallIdResponseDto.java} (77%) rename CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/dto/{CprCallNearUserDto.java => cpr_call/CprCallNearUserResponseDto.java} (71%) rename CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/dto/{CprCallOccurDto.java => cpr_call/CprCallRequestDto.java} (71%) rename CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/dto/{CprCallDto.java => cpr_call/CprCallResponseDto.java} (67%) rename CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/dto/{ => dispatch}/DispatchRequestDto.java (89%) rename CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/dto/{ => dispatch}/DispatchResponseDto.java (73%) create mode 100644 CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/repository/DispatchDslRepository.java create mode 100644 CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/repository/DispatchRepositoryImpl.java delete mode 100644 CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/service/FirebaseCloudMessageService.java delete mode 100644 CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/domain/EducationProgress.java create mode 100644 CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/domain/progress/EducationProgress.java create mode 100644 CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/domain/progress/LectureProgress.java create mode 100644 CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/domain/progress/PostureProgress.java create mode 100644 CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/domain/progress/QuizProgress.java rename CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/dto/{EducationProgressDto.java => ProgressResponseDto.java} (54%) rename CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/dto/{ScoreDto.java => ScoreRequestDto.java} (92%) rename CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/dto/{LectureProgressDto.java => lecture/LectureListResponseDto.java} (53%) delete mode 100644 CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/dto/lecture/PostureLectureResponseDto.java rename CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/domain/{AngelStatusEnum.java => AngelStatus.java} (89%) create mode 100644 CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/domain/Certificate.java delete mode 100644 CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/UserNicknameDto.java rename CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/{ => address}/AddressRequestDto.java (88%) rename CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/{ => address}/AddressResponseDto.java (90%) rename CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/{ => address}/SigugunResponseDto.java (87%) rename CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/{UserCodeDto.java => user/CodeResponseDto.java} (79%) rename CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/{UserLoginDto.java => user/LoginRequestDto.java} (85%) rename CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/{UserPhoneNumberDto.java => user/PhoneNumberRequestDto.java} (81%) rename CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/{UserSignUpDto.java => user/SignUpRequestDto.java} (74%) rename CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/{UserTokenReissueDto.java => user/TokenReissueRequestDto.java} (80%) rename CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/{UserTokenDto.java => user/TokenResponseDto.java} (83%) delete mode 100644 CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/repository/AddressDslRepository.java delete mode 100644 CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/repository/DeviceTokenDslRepository.java delete mode 100644 CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/repository/DeviceTokenRepositoryImpl.java create mode 100644 CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/repository/address/AddressDslRepository.java rename CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/repository/{ => address}/AddressRepository.java (71%) rename CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/repository/{ => address}/AddressRepositoryImpl.java (72%) create mode 100644 CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/repository/device_token/DeviceTokenDslRepository.java rename CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/repository/{ => device_token}/DeviceTokenRepository.java (87%) create mode 100644 CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/repository/device_token/DeviceTokenRepositoryImpl.java create mode 100644 CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/service/AuthService.java create mode 100644 CPR2U-Server/src/main/java/com/mentionall/cpr2u/util/CsvFileParser.java create mode 100644 CPR2U-Server/src/main/java/com/mentionall/cpr2u/util/fcm/FcmDataType.java rename CPR2U-Server/src/main/java/com/mentionall/cpr2u/util/{MessageEnum.java => fcm/FcmMessage.java} (85%) rename CPR2U-Server/src/main/java/com/mentionall/cpr2u/{call/dto/FcmPushTypeEnum.java => util/fcm/FcmPushType.java} (73%) create mode 100644 CPR2U-Server/src/main/java/com/mentionall/cpr2u/util/fcm/FirebaseCloudMessageUtil.java create mode 100644 CPR2U-Server/src/main/java/com/mentionall/cpr2u/util/twilio/FakeTwilioUtil.java create mode 100644 CPR2U-Server/src/main/java/com/mentionall/cpr2u/util/twilio/TwilioUtil.java create mode 100644 CPR2U-Server/src/main/resources/application.properties delete mode 100644 CPR2U-Server/src/main/resources/application.yml create mode 100644 CPR2U-Server/src/main/resources/cpr2u_address_table.csv create mode 100644 CPR2U-Server/src/test/java/com/mentionall/cpr2u/TestConfig.java delete mode 100644 CPR2U-Server/src/test/java/com/mentionall/cpr2u/call/repository/FakeCprCallRepository.java delete mode 100644 CPR2U-Server/src/test/java/com/mentionall/cpr2u/call/repository/FakeDispatchRepository.java delete mode 100644 CPR2U-Server/src/test/java/com/mentionall/cpr2u/call/repository/FakeReportRepository.java create mode 100644 CPR2U-Server/src/test/java/com/mentionall/cpr2u/call/util/FakeFirebaseCloudMessageUtil.java delete mode 100644 CPR2U-Server/src/test/java/com/mentionall/cpr2u/education/repository/FakeEducationProgressRepository.java delete mode 100644 CPR2U-Server/src/test/java/com/mentionall/cpr2u/education/repository/FakeLectureRepository.java delete mode 100644 CPR2U-Server/src/test/java/com/mentionall/cpr2u/education/repository/FakeQuizRepository.java create mode 100644 CPR2U-Server/src/test/java/com/mentionall/cpr2u/manager/service/ManagerServiceTest.java delete mode 100644 CPR2U-Server/src/test/java/com/mentionall/cpr2u/user/repository/FakeAddressRepository.java delete mode 100644 CPR2U-Server/src/test/java/com/mentionall/cpr2u/user/repository/FakeDeviceTokenRepository.java delete mode 100644 CPR2U-Server/src/test/java/com/mentionall/cpr2u/user/repository/FakeUserRepository.java delete mode 100644 CPR2U-Server/src/test/resources/application.yml create mode 100644 CPR2U-Server/src/test/resources/config/application.yml diff --git a/CPR2U-Server/.gitignore b/CPR2U-Server/.gitignore index ca4ef66..86f3351 100644 --- a/CPR2U-Server/.gitignore +++ b/CPR2U-Server/.gitignore @@ -33,4 +33,4 @@ out/ /.nb-gradle/ ### VS Code ### -.vscode/ \ No newline at end of file +.vscode/ diff --git a/CPR2U-Server/Dockerfile b/CPR2U-Server/Dockerfile index 6530b75..3c9a5f2 100644 --- a/CPR2U-Server/Dockerfile +++ b/CPR2U-Server/Dockerfile @@ -1,4 +1,4 @@ FROM adoptopenjdk:11-jre-hotspot ARG JAR_FILE=./build/libs/app.jar COPY ${JAR_FILE} app.jar -ENTRYPOINT ["java","-jar","/app.jar"] +ENTRYPOINT ["java","-jar","-Duser.timezone=Asia/Seoul", "/app.jar"] diff --git a/CPR2U-Server/README.md b/CPR2U-Server/README.md index e3ad3a7..9d24221 100644 --- a/CPR2U-Server/README.md +++ b/CPR2U-Server/README.md @@ -1 +1,99 @@ -# cpr2u-server +# 2023 Solution Challenge: CPR2U + + + `CPR2U` is an education solution to solve Korean's low CPR performance and survival rates for out-of-hostpital cradiac arrest patients. Users can receive CPR education through online and receive their CPR postures feedback through machine learning. In case of cardiac arrest, users can call for nearby certified users who have completed our training course to request emergency CPR. + +
+ +
+ +## πŸ“± How to run our app service +- Click [here](https://drive.google.com/drive/folders/1_wUgyb4SwmRlgZxCLX3tP-cZ9-uzlA8I?usp=share_link) to download an apk file. +- Install the apk file on your android smartphone. +- Enter `1111` in the verification code field to log in our service. (We temporarily removed the verification code sending API due to cost issue..) + +
+ +
+ +## πŸ–Œ Introduction + In Korea where the aging is fast, the number of patients with sudden cardiac arrest is expected to increase. In those cases, the survival rate could increase 2.4 times higher if CPR is performed by a bystander. However, the actual rate of CPR performed in Korea is only 24.7%. According to a study done by the Korea Centers for Disease Control and Prevention, 94% of people were aware of CPR, but only about 25% of them had actually learned it. Furthermore, among those people, over 53.6% of them have not received any training in the past two years. + + Firstly, we will make it easier for general public to receive CPR education. Since the rate of CPR graduated is declined nearly 80% after the pandemic, we believe the opportunity to learn cpr through online should be expanded to people, rather than through offline. Next, We want to make people take turns as much as they can during CPR in the emergency. CPR is a physically demanding processing that requires 120 compressions per minute. According to the guidelines, it is safe to replace the person compressing every two minutes. Therefore, we want to find people who can do CPR nearby and send them to emergencies. + +
+ +
+ +## βœ” UN SDGs Goals & Target +- 3. Good Health and Well-being (Target 3.4.) +- 4. Quality Education (Target 4.7.) + +
+ +
+ +## πŸ”§ Google Technology Used +- Android / Kotlin +- Firebase +- Tensorflow Lite +- Google Cloud Platform(Google Container Registry, Google Cloud SQL, Google Kubernetes Engine) +- Google Maps Platform + +
+ +
+ +## πŸ›  Project Architecture + + +
+ +
+ +## πŸ—‚ ERD + + +## πŸ“Έ Screenshots + + + +
+ + + +
+ + + +
+ + + +
+ + + +
+ + + +
+ + + +
+ + + +
+ + + +
+ + + +
+ + diff --git a/CPR2U-Server/build.gradle b/CPR2U-Server/build.gradle index 3f2e565..4f53700 100644 --- a/CPR2U-Server/build.gradle +++ b/CPR2U-Server/build.gradle @@ -8,6 +8,7 @@ plugins { id 'java' id 'org.springframework.boot' version '2.7.9' id 'io.spring.dependency-management' version '1.0.15.RELEASE' + id 'jacoco' } group = 'com.mentionall' @@ -43,15 +44,16 @@ dependencies { implementation 'com.google.firebase:firebase-admin:6.8.1' implementation group: 'com.squareup.okhttp3', name: 'okhttp', version: '4.2.2' - compileOnly 'org.projectlombok:lombok' + implementation 'com.twilio.sdk:twilio:8.8.0' - runtimeOnly 'com.h2database:h2' + compileOnly 'org.projectlombok:lombok' runtimeOnly 'com.mysql:mysql-connector-j' annotationProcessor 'org.projectlombok:lombok' annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor' testImplementation 'org.springframework.boot:spring-boot-starter-test' + testImplementation 'com.h2database:h2:1.4.200' } sourceSets { @@ -60,6 +62,18 @@ sourceSets { srcDirs = ["$projectDir/src/main/java", "$projectDir/build/generated"] } } + test{ + java { + srcDirs = ["$projectDir/src/test", "$projectDir/build/generated"] + } + resources { + includes = ["$projectDir/src/test/resources/config/application.yml"] + } + } +} + +test { + classpath += sourceSets.test.output } bootJar { @@ -68,4 +82,57 @@ bootJar { tasks.named('test') { useJUnitPlatform() + finalizedBy 'jacocoTestReport' + finalizedBy 'jacocoTestCoverageVerification' +} + +jacoco { + toolVersion = '0.8.7' +} + +jacocoTestReport { + reports { + xml.enabled true + csv.enabled true + html.enabled true + + xml.destination file("${buildDir}/jacoco/index.xml") + csv.destination file("${buildDir}/jacoco/index.csv") + html.destination file("${buildDir}/jacoco/index.html") + } + + afterEvaluate { + classDirectories.setFrom( + files(classDirectories.files.collect { + fileTree(dir: it, excludes: [ + '**/*Application*', + '**/*Exception*', + '**/dto/**', + '**/controller/**' + ]) + }) + ) + } } + +jacocoTestCoverageVerification { + var Qdomains = new ArrayList() + + for (char qPattern = 'A'; qPattern <= 'Z'; qPattern++) { + Qdomains.add("*.Q${qPattern}*") + } + + violationRules { + rule { + element = 'CLASS' + + limit { + counter = 'BRANCH' + value = 'COVEREDRATIO' + minimum = 0.00 + } + + excludes = Qdomains + } + } +} \ No newline at end of file diff --git a/CPR2U-Server/images/cpr2u_erd.png b/CPR2U-Server/images/cpr2u_erd.png new file mode 100644 index 0000000000000000000000000000000000000000..61bb64586550081b4eee883be641be6513a8a5a3 GIT binary patch literal 285119 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⪚SBwFp(*2JwUL~{2F+u8^E^@e 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≧djneBK#`{ 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©dXnL| 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 literal 0 HcmV?d00001 diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/Cpr2uApplication.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/Cpr2uApplication.java index 1245c58..f0cc939 100644 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/Cpr2uApplication.java +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/Cpr2uApplication.java @@ -5,12 +5,15 @@ import org.springframework.data.jpa.repository.config.EnableJpaAuditing; import org.springframework.scheduling.annotation.EnableScheduling; +import java.util.TimeZone; + @EnableJpaAuditing @SpringBootApplication @EnableScheduling public class Cpr2uApplication { public static void main(String[] args) { + TimeZone.setDefault(TimeZone.getTimeZone("Asia/Seoul")); SpringApplication.run(Cpr2uApplication.class, args); } diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/controller/CprCallController.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/controller/CprCallController.java index 69f23db..1dea7b3 100644 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/controller/CprCallController.java +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/controller/CprCallController.java @@ -1,9 +1,9 @@ package com.mentionall.cpr2u.call.controller; -import com.mentionall.cpr2u.call.dto.CprCallGuideResponseDto; -import com.mentionall.cpr2u.call.dto.CprCallIdDto; -import com.mentionall.cpr2u.call.dto.CprCallNearUserDto; -import com.mentionall.cpr2u.call.dto.CprCallOccurDto; +import com.mentionall.cpr2u.call.dto.cpr_call.CprCallGuideResponseDto; +import com.mentionall.cpr2u.call.dto.cpr_call.CprCallIdResponseDto; +import com.mentionall.cpr2u.call.dto.cpr_call.CprCallNearUserResponseDto; +import com.mentionall.cpr2u.call.dto.cpr_call.CprCallRequestDto; import com.mentionall.cpr2u.call.service.CprCallService; import com.mentionall.cpr2u.user.domain.PrincipalDetails; import com.mentionall.cpr2u.util.GetUserDetails; @@ -33,7 +33,7 @@ public class CprCallController { @Operation(summary = "ν™ˆ ν™”λ©΄", description = "ν˜„μž¬ κ·Όμ²˜μ—μ„œ 진행쀑인 CPR μš”μ²­μ΄ μžˆλŠ”μ§€ ν™•μΈν•œλ‹€.") @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "성곡", content = @Content(array = @ArraySchema(schema = @Schema(implementation = CprCallNearUserDto.class)))), + @ApiResponse(responseCode = "200", description = "성곡", content = @Content(array = @ArraySchema(schema = @Schema(implementation = CprCallNearUserResponseDto.class)))), @ApiResponse(responseCode = "400", description = "μ—”μ € 자격증이 μžˆμœΌλ‚˜, μ£Όμ†Œλ₯Ό μ„€μ •ν•˜μ§€ μ•ŠμŒ", content = @Content(array = @ArraySchema(schema = @Schema(implementation = ResponseTemplate.class)))) }) @GetMapping @@ -43,13 +43,13 @@ public ResponseEntity getNowCallStatusNearUser(@GetUserDet @Operation(summary = "ν˜ΈμΆœν•˜κΈ°", description = "사건 λ°œμƒ μ§€μ—­μ˜ CPR Angel듀을 ν˜ΈμΆœν•œλ‹€.") @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "성곡", content = @Content(array = @ArraySchema(schema = @Schema(implementation = CprCallIdDto.class)))), - @ApiResponse(responseCode = "404", description = "ν•΄λ‹Ή μ£Όμ†Œμ§€μ— λ§žλŠ” μ£Όμ†Œ 지역ꡬλ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€", content = @Content(array = @ArraySchema(schema = @Schema(implementation = CprCallIdDto.class)))) + @ApiResponse(responseCode = "200", description = "성곡", content = @Content(array = @ArraySchema(schema = @Schema(implementation = CprCallIdResponseDto.class)))), + @ApiResponse(responseCode = "404", description = "ν•΄λ‹Ή μ£Όμ†Œμ§€μ— λ§žλŠ” μ£Όμ†Œ 지역ꡬλ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€", content = @Content(array = @ArraySchema(schema = @Schema(implementation = CprCallIdResponseDto.class)))) }) @PostMapping - public ResponseEntity makeCall(@RequestBody CprCallOccurDto cprCallOccurDto, + public ResponseEntity makeCall(@RequestBody CprCallRequestDto cprCallRequestDto, @GetUserDetails PrincipalDetails userDetails) { - return ResponseDataTemplate.toResponseEntity(OK_SUCCESS, cprCallService.makeCall(cprCallOccurDto, userDetails.getUser())); + return ResponseDataTemplate.toResponseEntity(OK_SUCCESS, cprCallService.makeCall(cprCallRequestDto, userDetails.getUser())); } @Operation(summary = "μ‹€μ‹œκ°„ 호좜 상황 μ•ˆλ‚΄", description = "ν˜„μž¬ μΆœλ™ 쀑인 CPR 엔저이 λͺ‡λͺ…인지 ν™•μΈν•œλ‹€.") diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/controller/DispatchController.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/controller/DispatchController.java index 1793966..d21e30f 100644 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/controller/DispatchController.java +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/controller/DispatchController.java @@ -1,7 +1,7 @@ package com.mentionall.cpr2u.call.controller; -import com.mentionall.cpr2u.call.dto.DispatchRequestDto; -import com.mentionall.cpr2u.call.dto.DispatchResponseDto; +import com.mentionall.cpr2u.call.dto.dispatch.DispatchRequestDto; +import com.mentionall.cpr2u.call.dto.dispatch.DispatchResponseDto; import com.mentionall.cpr2u.call.dto.ReportRequestDto; import com.mentionall.cpr2u.call.service.DispatchService; import com.mentionall.cpr2u.user.domain.PrincipalDetails; @@ -34,6 +34,8 @@ public class DispatchController { @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성곡", content = @Content(array = @ArraySchema(schema = @Schema(implementation = DispatchResponseDto.class)))), + @ApiResponse(responseCode = "400", description = "이미 μ’…λ£Œλ˜μ—ˆκ±°λ‚˜, 본인이 μƒμ„±ν•œ ν˜ΈμΆœμ— λŒ€ν•œ μΆœλ™μž…λ‹ˆλ‹€.", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ResponseTemplate.class)))), @ApiResponse(responseCode = "404", description = "ν•΄λ‹Ή ID의 CPR μš”μ²­ 정보λ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.", content = @Content(array = @ArraySchema(schema = @Schema(implementation = ResponseTemplate.class)))), }) diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/domain/CprCall.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/domain/CprCall.java index 97770b2..71a45e6 100644 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/domain/CprCall.java +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/domain/CprCall.java @@ -1,6 +1,6 @@ package com.mentionall.cpr2u.call.domain; -import com.mentionall.cpr2u.call.dto.CprCallOccurDto; +import com.mentionall.cpr2u.call.dto.cpr_call.CprCallRequestDto; import com.mentionall.cpr2u.user.domain.Address; import com.mentionall.cpr2u.user.domain.User; import lombok.AllArgsConstructor; @@ -54,13 +54,13 @@ public class CprCall { @OneToMany(cascade = CascadeType.ALL, mappedBy = "cprCall") List reportList = new ArrayList<>(); - public CprCall(User user, Address address, LocalDateTime calledAt, CprCallOccurDto cprCallOccurDto) { + public CprCall(User user, Address address, LocalDateTime calledAt, CprCallRequestDto cprCallRequestDto) { this.caller = user; this.address = address; - this.fullAddress = cprCallOccurDto.getFullAddress(); + this.fullAddress = cprCallRequestDto.getFullAddress(); this.calledAt = calledAt; - this.latitude = cprCallOccurDto.getLatitude(); - this.longitude = cprCallOccurDto.getLongitude(); + this.latitude = cprCallRequestDto.getLatitude(); + this.longitude = cprCallRequestDto.getLongitude(); this.status = CprCallStatus.IN_PROGRESS; } diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/domain/Dispatch.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/domain/Dispatch.java index 222c7e6..a2e7322 100644 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/domain/Dispatch.java +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/domain/Dispatch.java @@ -34,7 +34,7 @@ public class Dispatch { private LocalDateTime arrivedAt; @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "cpr_call") + @JoinColumn(name = "cpr_call_id") private CprCall cprCall; diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/dto/FcmMessage.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/dto/FcmMessage.java deleted file mode 100644 index 6ca9018..0000000 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/dto/FcmMessage.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.mentionall.cpr2u.call.dto; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; - -import java.util.Map; - -@Builder -@AllArgsConstructor -@Getter -public class FcmMessage { - private boolean validateOnly; - private Message message; - - @Builder - @AllArgsConstructor - @Getter - public static class Message { - private Notification notification; - private String token; - private Map data; - } - - @Builder - @AllArgsConstructor - @Getter - public static class Notification { - private String title; - private String body; - } -} \ No newline at end of file diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/dto/CprCallGuideResponseDto.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/dto/cpr_call/CprCallGuideResponseDto.java similarity index 82% rename from CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/dto/CprCallGuideResponseDto.java rename to CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/dto/cpr_call/CprCallGuideResponseDto.java index e4abe1a..25dfb46 100644 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/dto/CprCallGuideResponseDto.java +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/dto/cpr_call/CprCallGuideResponseDto.java @@ -1,12 +1,10 @@ -package com.mentionall.cpr2u.call.dto; +package com.mentionall.cpr2u.call.dto.cpr_call; import com.fasterxml.jackson.annotation.JsonProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.AllArgsConstructor; import lombok.Data; -import java.time.LocalDateTime; - @Data @AllArgsConstructor public class CprCallGuideResponseDto { diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/dto/CprCallIdDto.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/dto/cpr_call/CprCallIdResponseDto.java similarity index 77% rename from CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/dto/CprCallIdDto.java rename to CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/dto/cpr_call/CprCallIdResponseDto.java index 64c9a45..665eb12 100644 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/dto/CprCallIdDto.java +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/dto/cpr_call/CprCallIdResponseDto.java @@ -1,4 +1,4 @@ -package com.mentionall.cpr2u.call.dto; +package com.mentionall.cpr2u.call.dto.cpr_call; import com.fasterxml.jackson.annotation.JsonProperty; import io.swagger.v3.oas.annotations.media.Schema; @@ -7,7 +7,7 @@ @Data @AllArgsConstructor -public class CprCallIdDto { +public class CprCallIdResponseDto { @Schema(description = "호좜 id") @JsonProperty("call_id") private Long callId; diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/dto/CprCallNearUserDto.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/dto/cpr_call/CprCallNearUserResponseDto.java similarity index 71% rename from CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/dto/CprCallNearUserDto.java rename to CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/dto/cpr_call/CprCallNearUserResponseDto.java index 884d03c..777ed83 100644 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/dto/CprCallNearUserDto.java +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/dto/cpr_call/CprCallNearUserResponseDto.java @@ -1,7 +1,7 @@ -package com.mentionall.cpr2u.call.dto; +package com.mentionall.cpr2u.call.dto.cpr_call; import com.fasterxml.jackson.annotation.JsonProperty; -import com.mentionall.cpr2u.user.domain.AngelStatusEnum; +import com.mentionall.cpr2u.user.domain.AngelStatus; import io.swagger.v3.oas.annotations.media.Schema; import lombok.AllArgsConstructor; import lombok.Data; @@ -13,11 +13,11 @@ @Data @AllArgsConstructor -public class CprCallNearUserDto { +public class CprCallNearUserResponseDto { @Schema(description = "μ‚¬μš©μžμ˜ ν˜„μž¬ 자격") @JsonProperty("angel_status") @Enumerated(EnumType.STRING) - private AngelStatusEnum angelStatus; + private AngelStatus angelStatus; @Schema(description = "근처 ν™˜μž 유무") @JsonProperty("is_patient") @@ -25,7 +25,7 @@ public class CprCallNearUserDto { @Schema(description = "근처 ν™˜μž 정보 리슀트") @JsonProperty("call_list") - private List CprCallDtoList = new ArrayList<>(); + private List cprCallResponseDtoList = new ArrayList<>(); } diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/dto/CprCallOccurDto.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/dto/cpr_call/CprCallRequestDto.java similarity index 71% rename from CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/dto/CprCallOccurDto.java rename to CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/dto/cpr_call/CprCallRequestDto.java index 3e191a0..dc19454 100644 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/dto/CprCallOccurDto.java +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/dto/cpr_call/CprCallRequestDto.java @@ -1,19 +1,15 @@ -package com.mentionall.cpr2u.call.dto; +package com.mentionall.cpr2u.call.dto.cpr_call; import com.fasterxml.jackson.annotation.JsonProperty; -import com.mentionall.cpr2u.call.domain.CprCall; -import com.querydsl.core.annotations.QueryProjection; import io.swagger.v3.oas.annotations.media.Schema; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; -import java.time.format.DateTimeFormatter; - @Data @AllArgsConstructor @NoArgsConstructor -public class CprCallOccurDto { +public class CprCallRequestDto { @Schema(description = "전체 μ£Όμ†Œ") @JsonProperty("full_address") diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/dto/CprCallDto.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/dto/cpr_call/CprCallResponseDto.java similarity index 67% rename from CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/dto/CprCallDto.java rename to CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/dto/cpr_call/CprCallResponseDto.java index d477268..267ad71 100644 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/dto/CprCallDto.java +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/dto/cpr_call/CprCallResponseDto.java @@ -1,15 +1,16 @@ -package com.mentionall.cpr2u.call.dto; +package com.mentionall.cpr2u.call.dto.cpr_call; +import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonProperty; import com.mentionall.cpr2u.call.domain.CprCall; import com.querydsl.core.annotations.QueryProjection; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; -import java.time.format.DateTimeFormatter; +import java.time.LocalDateTime; @Data -public class CprCallDto { +public class CprCallResponseDto { @Schema(description = "호좜 id") @JsonProperty("cpr_call_id") private Long id; @@ -20,7 +21,8 @@ public class CprCallDto { @Schema(description = "호좜 μ‹œμž‘ν•œ μ‹œκ°„") @JsonProperty("called_at") - private String calledAt; + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Seoul") + private LocalDateTime calledAt; @Schema(description = "호좜 μž₯μ†Œ μœ„λ„") @JsonProperty @@ -28,13 +30,13 @@ public class CprCallDto { @Schema(description = "호좜 μž₯μ†Œ 경도") @JsonProperty - private Double longitude; + private Double longitude; @QueryProjection - public CprCallDto(CprCall cprCall){ + public CprCallResponseDto(CprCall cprCall) { this.id = cprCall.getId(); this.fullAddress = cprCall.getFullAddress(); - this.calledAt = cprCall.getCalledAt().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + this.calledAt = cprCall.getCalledAt(); this.latitude = cprCall.getLatitude(); this.longitude = cprCall.getLongitude(); } diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/dto/DispatchRequestDto.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/dto/dispatch/DispatchRequestDto.java similarity index 89% rename from CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/dto/DispatchRequestDto.java rename to CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/dto/dispatch/DispatchRequestDto.java index 9b3f32c..d6ce08d 100644 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/dto/DispatchRequestDto.java +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/dto/dispatch/DispatchRequestDto.java @@ -1,4 +1,4 @@ -package com.mentionall.cpr2u.call.dto; +package com.mentionall.cpr2u.call.dto.dispatch; import com.fasterxml.jackson.annotation.JsonProperty; import io.swagger.v3.oas.annotations.media.Schema; diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/dto/DispatchResponseDto.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/dto/dispatch/DispatchResponseDto.java similarity index 73% rename from CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/dto/DispatchResponseDto.java rename to CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/dto/dispatch/DispatchResponseDto.java index 34c9938..b838caa 100644 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/dto/DispatchResponseDto.java +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/dto/dispatch/DispatchResponseDto.java @@ -1,5 +1,6 @@ -package com.mentionall.cpr2u.call.dto; +package com.mentionall.cpr2u.call.dto.dispatch; +import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonProperty; import com.mentionall.cpr2u.call.domain.CprCall; import com.mentionall.cpr2u.call.domain.Dispatch; @@ -13,21 +14,22 @@ public class DispatchResponseDto { @Schema(description = "μΆœλ™ 데이터 ID") @JsonProperty("dispatch_id") - Long dispatchId; + private Long dispatchId; @Schema(description = "CPR μš”μ²­ μ£Όμ†Œμ§€") @JsonProperty("full_address") - String fullAddress; + private String fullAddress; @Schema(description = "μš”μ²­ μ‹œμž‘ μ‹œκ°„") @JsonProperty("called_at") - LocalDateTime calledAt; + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Seoul") + private LocalDateTime calledAt; @Schema(description = "CPR μš”μ²­ μž₯μ†Œ μœ„λ„") - Double latitude; + private Double latitude; @Schema(description = "CPR μš”μ²­ μž₯μ†Œ 경도") - Double longitude; + private Double longitude; public DispatchResponseDto(CprCall cprCall, Dispatch dispatch) { this.dispatchId = dispatch.getId(); diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/repository/CprCallDslRepository.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/repository/CprCallDslRepository.java index 6d978ec..ceaf05f 100644 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/repository/CprCallDslRepository.java +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/repository/CprCallDslRepository.java @@ -1,12 +1,12 @@ package com.mentionall.cpr2u.call.repository; import com.mentionall.cpr2u.call.domain.CprCall; -import com.mentionall.cpr2u.call.dto.CprCallDto; +import com.mentionall.cpr2u.call.dto.cpr_call.CprCallResponseDto; import java.util.List; public interface CprCallDslRepository { - List findAllCallInProcessByAddress(Long addressId); + List findAllCallInProcessByAddress(Long addressId); List findAllCallInProgressButExpired(); } diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/repository/CprCallRepositoryImpl.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/repository/CprCallRepositoryImpl.java index 62ec31a..44d3a87 100644 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/repository/CprCallRepositoryImpl.java +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/repository/CprCallRepositoryImpl.java @@ -2,8 +2,8 @@ import com.mentionall.cpr2u.call.domain.CprCall; import com.mentionall.cpr2u.call.domain.CprCallStatus; -import com.mentionall.cpr2u.call.dto.CprCallDto; -import com.mentionall.cpr2u.call.dto.QCprCallDto; +import com.mentionall.cpr2u.call.dto.cpr_call.CprCallResponseDto; +import com.mentionall.cpr2u.call.dto.cpr_call.QCprCallResponseDto; import com.querydsl.jpa.impl.JPAQueryFactory; import javax.persistence.EntityManager; @@ -21,8 +21,8 @@ public CprCallRepositoryImpl(EntityManager em) { } @Override - public List findAllCallInProcessByAddress(Long addressId) { - return queryFactory.select(new QCprCallDto(cprCall)) + public List findAllCallInProcessByAddress(Long addressId) { + return queryFactory.select(new QCprCallResponseDto(cprCall)) .from(cprCall) .where(cprCall.status.eq(CprCallStatus.IN_PROGRESS).and(cprCall.address.id.eq(addressId))) .fetch(); diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/repository/DispatchDslRepository.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/repository/DispatchDslRepository.java new file mode 100644 index 0000000..580454b --- /dev/null +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/repository/DispatchDslRepository.java @@ -0,0 +1,11 @@ +package com.mentionall.cpr2u.call.repository; + +import com.mentionall.cpr2u.call.domain.Dispatch; + +import java.util.List; +import java.util.Optional; + +public interface DispatchDslRepository { + List findAllNotArrivedAngelByCprCallId(Long cprCallId); + +} diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/repository/DispatchRepository.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/repository/DispatchRepository.java index d48a67c..49718c8 100644 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/repository/DispatchRepository.java +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/repository/DispatchRepository.java @@ -2,9 +2,13 @@ import com.mentionall.cpr2u.call.domain.Dispatch; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.querydsl.QuerydslPredicateExecutor; import java.util.List; +import java.util.Optional; -public interface DispatchRepository extends JpaRepository { +public interface DispatchRepository extends JpaRepository , DispatchDslRepository, QuerydslPredicateExecutor { List findAllByCprCallId(Long cprCallId); + Optional findByCprCallIdAndDispatcherId(Long cprCallId, String dispatcher); } + diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/repository/DispatchRepositoryImpl.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/repository/DispatchRepositoryImpl.java new file mode 100644 index 0000000..9303a4b --- /dev/null +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/repository/DispatchRepositoryImpl.java @@ -0,0 +1,27 @@ +package com.mentionall.cpr2u.call.repository; + +import com.mentionall.cpr2u.call.domain.Dispatch; +import com.mentionall.cpr2u.call.domain.DispatchStatus; +import com.querydsl.jpa.impl.JPAQueryFactory; + +import javax.persistence.EntityManager; +import java.util.List; + +import static com.mentionall.cpr2u.call.domain.QDispatch.dispatch; + +public class DispatchRepositoryImpl implements DispatchDslRepository { + + private final JPAQueryFactory queryFactory; + + public DispatchRepositoryImpl(EntityManager em) { + this.queryFactory = new JPAQueryFactory(em); + } + + @Override + public List findAllNotArrivedAngelByCprCallId(Long cprCallId) { + return queryFactory.selectFrom(dispatch) + .where(dispatch.status.eq(DispatchStatus.IN_PROGRESS) + .and(dispatch.cprCall.id.eq(cprCallId))) + .fetch(); + } +} diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/repository/ReportRepository.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/repository/ReportRepository.java index 69a78b3..4b4d1ec 100644 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/repository/ReportRepository.java +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/repository/ReportRepository.java @@ -1,5 +1,6 @@ package com.mentionall.cpr2u.call.repository; +import com.mentionall.cpr2u.call.domain.CprCall; import com.mentionall.cpr2u.call.domain.Report; import com.mentionall.cpr2u.user.domain.User; import org.springframework.data.jpa.repository.JpaRepository; @@ -9,5 +10,5 @@ @Repository public interface ReportRepository extends JpaRepository { - public List findAllByReporter(User user); + List findAllByReporter(User reporter); } diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/service/CprCallService.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/service/CprCallService.java index 8a72b94..3a4d960 100644 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/service/CprCallService.java +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/service/CprCallService.java @@ -3,25 +3,35 @@ import com.mentionall.cpr2u.call.domain.CprCall; import com.mentionall.cpr2u.call.domain.Dispatch; import com.mentionall.cpr2u.call.domain.DispatchStatus; -import com.mentionall.cpr2u.call.dto.*; +import com.mentionall.cpr2u.call.dto.cpr_call.*; import com.mentionall.cpr2u.call.repository.CprCallRepository; import com.mentionall.cpr2u.call.repository.DispatchRepository; import com.mentionall.cpr2u.user.domain.Address; -import com.mentionall.cpr2u.user.domain.AngelStatusEnum; -import com.mentionall.cpr2u.user.domain.DeviceToken; +import com.mentionall.cpr2u.user.domain.AngelStatus; import com.mentionall.cpr2u.user.domain.User; -import com.mentionall.cpr2u.user.repository.AddressRepository; -import com.mentionall.cpr2u.user.repository.DeviceTokenRepository; -import com.mentionall.cpr2u.util.MessageEnum; +import com.mentionall.cpr2u.user.repository.address.AddressRepository; +import com.mentionall.cpr2u.user.repository.device_token.DeviceTokenRepository; import com.mentionall.cpr2u.util.exception.CustomException; import com.mentionall.cpr2u.util.exception.ResponseCode; +import com.mentionall.cpr2u.util.fcm.FcmDataType; +import com.mentionall.cpr2u.util.fcm.FcmMessage; +import com.mentionall.cpr2u.util.fcm.FcmPushType; +import com.mentionall.cpr2u.util.fcm.FirebaseCloudMessageUtil; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; -import java.io.IOException; import java.time.LocalDateTime; -import java.util.*; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +@Slf4j @Service @RequiredArgsConstructor public class CprCallService { @@ -29,71 +39,35 @@ public class CprCallService { private final DispatchRepository dispatchRepository; private final AddressRepository addressRepository; private final DeviceTokenRepository deviceTokenRepository; - private final FirebaseCloudMessageService firebaseCloudMessageService; + private final FirebaseCloudMessageUtil firebaseCloudMessageUtil; - public CprCallNearUserDto getCallNearUser(User user) { - AngelStatusEnum userAngelStatus = user.getStatus(); - if (userAngelStatus != AngelStatusEnum.ACQUIRED) { - return new CprCallNearUserDto( + public CprCallNearUserResponseDto getCallNearUser(User user) { + + AngelStatus userAngelStatus = user.getAngelStatus(); + if (userAngelStatus != AngelStatus.ACQUIRED) { + return new CprCallNearUserResponseDto( userAngelStatus, false, new ArrayList<>() ); } - if (user.getAddress() == null) { - throw new CustomException(ResponseCode.BAD_REQUEST_ADDRESS_NOT_SET); - } - List cprCallDtoList = cprCallRepository.findAllCallInProcessByAddress(user.getAddress().getId()); - return new CprCallNearUserDto( + + List cprCallResponseDtoList = cprCallRepository.findAllCallInProcessByAddress(user.getAddress().getId()); + return new CprCallNearUserResponseDto( userAngelStatus, - cprCallDtoList.size() > 0, - cprCallDtoList + cprCallResponseDtoList.size() > 0, + cprCallResponseDtoList ); } - public CprCallIdDto makeCall(CprCallOccurDto cprCallOccurDto, User user) { - Address callAddress = addressRepository.findByFullAddress(cprCallOccurDto.getFullAddress().split(" ")) - .orElseThrow(() -> new CustomException(ResponseCode.NOT_FOUND_FAILED_TO_MATCH_ADDRESS)); - - CprCall cprCall = new CprCall(user, callAddress, LocalDateTime.now(), cprCallOccurDto); - cprCallRepository.save(cprCall); - - List deviceTokenToSendPushList = deviceTokenRepository.findAllDeviceTokenByUserAddress(cprCall.getAddress().getId(), user.getId()); - for(DeviceToken deviceToken : deviceTokenToSendPushList){ - try { - firebaseCloudMessageService.sendMessageTo(deviceToken.getToken(), - MessageEnum.CPR_CALL_TITLE.getMessage(), - cprCall.getFullAddress(), - new LinkedHashMap<>(){{ - put("type", String.valueOf(FcmPushTypeEnum.CPR_CALL.ordinal())); - put("call", String.valueOf(cprCall.getId())); - }} - ); - } catch (IOException e) { - throw new CustomException(ResponseCode.SERVER_ERROR_FAILED_TO_SEND_FCM); - } - - Timer timer = new Timer(); - TimerTask task = new TimerTask() { - public void run() { - cprCall.endSituationCprCall(); - cprCallRepository.save(cprCall); - } - }; - - timer.schedule(task, 1000 * 60 * 10); - } - return new CprCallIdDto(cprCall.getId()); - } - public void endCall(Long callId) { CprCall cprCall = cprCallRepository.findById(callId).orElseThrow( () -> new CustomException(ResponseCode.NOT_FOUND_CPRCALL) ); cprCall.endSituationCprCall(); cprCallRepository.save(cprCall); - List dispatchList = dispatchRepository.findAllByCprCallId(cprCall.getId()); - for (Dispatch dispatch : dispatchList) { + List notArrivedDispatchList = dispatchRepository.findAllNotArrivedAngelByCprCallId(cprCall.getId()); + for (Dispatch dispatch : notArrivedDispatchList) { dispatch.setStatus(DispatchStatus.END_SITUATION); dispatchRepository.save(dispatch); } @@ -108,4 +82,58 @@ public CprCallGuideResponseDto getNumberOfAngelsDispatched(Long callId) { List dispatchList = dispatchRepository.findAllByCprCallId(callId); return new CprCallGuideResponseDto(dispatchList.size()); } + + public CprCallIdResponseDto makeCall(CprCallRequestDto cprCallRequestDto, User user) { + Address callAddress = addressRepository.findByFullAddress(cprCallRequestDto.getFullAddress()) + .orElseThrow(() -> new CustomException(ResponseCode.NOT_FOUND_FAILED_TO_MATCH_ADDRESS)); + + CprCall cprCall = new CprCall(user, callAddress, LocalDateTime.now(), cprCallRequestDto); + cprCallRepository.save(cprCall); + + sendFcmPushToAddress(cprCall, user.getId()); + + Integer minutesUntilCallDisappear = 15; + endCprCallAfterMinutes(cprCall, minutesUntilCallDisappear); + + return new CprCallIdResponseDto(cprCall.getId()); + } + + private void sendFcmPushToAddress(CprCall cprCall, String userId) { + + int offset = 0; + int maxSize = 500; + Pageable pageable; + + LinkedHashMap dataToSend = new LinkedHashMap<>() {{ + put(FcmDataType.TYPE.getType(), String.valueOf(FcmPushType.CPR_CALL.ordinal())); + put(FcmDataType.CPR_CALL_ID.getType(), String.valueOf(cprCall.getId())); + }}; + + List deviceTokenToSendPushList; + do { + pageable = PageRequest.of(offset, maxSize); + deviceTokenToSendPushList = deviceTokenRepository.findAllDeviceTokenByUserAddressExceptCaller(cprCall.getAddress().getId(), userId, pageable); + firebaseCloudMessageUtil.sendFcmMessage( + deviceTokenToSendPushList, + FcmMessage.CPR_CALL_TITLE.getMessage(), + cprCall.getFullAddress(), + dataToSend + ); + offset += maxSize; + } while (deviceTokenToSendPushList.size() >= maxSize); + + } + + private void endCprCallAfterMinutes(CprCall cprCall, Integer minutes) { + ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); + + Runnable task = () -> { + cprCall.endSituationCprCall(); + cprCallRepository.save(cprCall); + }; + + executor.schedule(task, minutes, TimeUnit.MINUTES); + executor.shutdown(); + } + } diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/service/DispatchService.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/service/DispatchService.java index 5396805..57dc558 100644 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/service/DispatchService.java +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/service/DispatchService.java @@ -1,11 +1,12 @@ package com.mentionall.cpr2u.call.service; import com.mentionall.cpr2u.call.domain.CprCall; +import com.mentionall.cpr2u.call.domain.CprCallStatus; import com.mentionall.cpr2u.call.domain.Dispatch; import com.mentionall.cpr2u.call.domain.Report; -import com.mentionall.cpr2u.call.dto.DispatchRequestDto; -import com.mentionall.cpr2u.call.dto.DispatchResponseDto; import com.mentionall.cpr2u.call.dto.ReportRequestDto; +import com.mentionall.cpr2u.call.dto.dispatch.DispatchRequestDto; +import com.mentionall.cpr2u.call.dto.dispatch.DispatchResponseDto; import com.mentionall.cpr2u.call.repository.CprCallRepository; import com.mentionall.cpr2u.call.repository.DispatchRepository; import com.mentionall.cpr2u.call.repository.ReportRepository; @@ -14,8 +15,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; -import static com.mentionall.cpr2u.util.exception.ResponseCode.NOT_FOUND_CPRCALL; -import static com.mentionall.cpr2u.util.exception.ResponseCode.NOT_FOUND_DISPATCH; +import static com.mentionall.cpr2u.util.exception.ResponseCode.*; @Service @RequiredArgsConstructor @@ -30,9 +30,11 @@ public DispatchResponseDto dispatch(User user, DispatchRequestDto requestDto) { () -> new CustomException(NOT_FOUND_CPRCALL) ); - Dispatch dispatch = new Dispatch(user, cprCall); - dispatchRepository.save(dispatch); + if(cprCall.getCaller().getId()== user.getId() || cprCall.getStatus().equals(CprCallStatus.END_SITUATION)) + throw new CustomException(BAD_REQUEST_NOT_VALID_DISPATCH); + Dispatch dispatch = dispatchRepository.findByCprCallIdAndDispatcherId(cprCall.getId(), user.getId()).orElseGet(() -> new Dispatch(user, cprCall)); + dispatchRepository.save(dispatch); return new DispatchResponseDto(cprCall, dispatch); } @@ -41,6 +43,7 @@ public void arrive(Long dispatchId) { () -> new CustomException(NOT_FOUND_DISPATCH) ); dispatch.arrive(); + dispatchRepository.save(dispatch); } public void report(ReportRequestDto requestDto) { diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/service/FirebaseCloudMessageService.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/service/FirebaseCloudMessageService.java deleted file mode 100644 index a4fe40d..0000000 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/service/FirebaseCloudMessageService.java +++ /dev/null @@ -1,70 +0,0 @@ -package com.mentionall.cpr2u.call.service; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.auth.oauth2.GoogleCredentials; -import com.mentionall.cpr2u.call.dto.FcmMessage; -import lombok.RequiredArgsConstructor; -import okhttp3.*; -import org.springframework.core.io.ClassPathResource; -import org.springframework.http.HttpHeaders; -import org.springframework.stereotype.Component; - -import java.io.IOException; -import java.util.List; -import java.util.Map; - -@Component -@RequiredArgsConstructor -public class FirebaseCloudMessageService { - - private final String API_URL = "https://fcm.googleapis.com/v1/projects/cpr2u-b1b0d/messages:send"; - private final ObjectMapper objectMapper; - - - public void sendMessageTo(String targetToken, String title, String body, Map data) throws IOException { - String message = makeMessage(targetToken, title, body, data); - - OkHttpClient client = new OkHttpClient(); - RequestBody requestBody = RequestBody.create(message, - MediaType.get("application/json; charset=utf-8")); - Request request = new Request.Builder() - .url(API_URL) - .post(requestBody) - .addHeader(HttpHeaders.AUTHORIZATION, "Bearer " + getAccessToken()) - .addHeader(HttpHeaders.CONTENT_TYPE, "application/json; UTF-8") - .build(); - - Response response = client.newCall(request).execute(); - System.out.println(response.body().string()); - response.close(); - - } - - - private String makeMessage(String targetToken, String title, String body, Map data) throws JsonProcessingException { - FcmMessage fcmMessage = FcmMessage.builder() - .message(FcmMessage.Message.builder() - .token(targetToken) - .data(data) - .notification(FcmMessage.Notification.builder() - .title(title) - .body(body) - .build() - ).build()).validateOnly(false).build(); - - return objectMapper.writeValueAsString(fcmMessage); - } - - private String getAccessToken() throws IOException { - String firebaseConfigPath = "firebase/firebase_service_key.json"; - - GoogleCredentials googleCredentials = GoogleCredentials - .fromStream(new ClassPathResource(firebaseConfigPath).getInputStream()) - .createScoped(List.of("https://www.googleapis.com/auth/cloud-platform")); - - googleCredentials.refreshIfExpired(); - return googleCredentials.getAccessToken().getTokenValue(); - } - -} \ No newline at end of file diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/config/security/JwtTokenProvider.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/config/security/JwtTokenProvider.java index 2ba49fa..800fcdf 100644 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/config/security/JwtTokenProvider.java +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/config/security/JwtTokenProvider.java @@ -1,6 +1,6 @@ package com.mentionall.cpr2u.config.security; -import com.mentionall.cpr2u.user.domain.UserRole; +import com.mentionall.cpr2u.user.domain.User; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jws; import io.jsonwebtoken.Jwts; @@ -17,7 +17,6 @@ import javax.servlet.http.HttpServletRequest; import java.util.Base64; import java.util.Date; -import java.util.List; @RequiredArgsConstructor @Component @@ -36,9 +35,9 @@ protected void init(){ secretKey = Base64.getEncoder().encodeToString(secretKey.getBytes()); } - public String createToken(String userPk, List roles) { - Claims claims = Jwts.claims().setSubject(userPk); - claims.put("roles", roles); + public String createAccessToken(User user) { + Claims claims = Jwts.claims().setSubject(user.getId()); + claims.put("roles", user.getRoles()); Date now = new Date(); return Jwts.builder() .setClaims(claims) @@ -48,9 +47,12 @@ public String createToken(String userPk, List roles) { .compact(); } - public String createRefreshToken() { + public String createRefreshToken(User user) { + Claims claims = Jwts.claims().setSubject(user.getId()); + Date now = new Date(); return Jwts.builder() + .setClaims(claims) .setIssuedAt(now) .setExpiration(new Date(now.getTime() + refreshTokenValidTime)) .signWith(SignatureAlgorithm.HS256, secretKey) diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/config/security/WebSecurityConfig.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/config/security/WebSecurityConfig.java index 2580319..82021de 100644 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/config/security/WebSecurityConfig.java +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/config/security/WebSecurityConfig.java @@ -50,9 +50,11 @@ protected void configure(HttpSecurity http) throws Exception { .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeRequests() + .antMatchers("/auth/logout") + .authenticated() .antMatchers("/", "/auth/**", "/manage/**", "/webjars/**", "/v3/api-docs", - "/swagger-ui/**", "/swagger/**", "/swagger-ui.html", "/swagger-resources/**", "/h2-console" - ).permitAll() + "/swagger-ui/**", "/swagger/**", "/swagger-ui.html", "/swagger-resources/**", "/h2-console") + .permitAll() .and() .authorizeRequests() .anyRequest() diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/controller/EducationController.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/controller/EducationController.java index b0fd223..08032d6 100644 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/controller/EducationController.java +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/controller/EducationController.java @@ -1,9 +1,8 @@ package com.mentionall.cpr2u.education.controller; -import com.mentionall.cpr2u.education.dto.EducationProgressDto; -import com.mentionall.cpr2u.education.dto.LectureProgressDto; -import com.mentionall.cpr2u.education.dto.ScoreDto; -import com.mentionall.cpr2u.education.dto.lecture.PostureLectureResponseDto; +import com.mentionall.cpr2u.education.dto.ProgressResponseDto; +import com.mentionall.cpr2u.education.dto.lecture.LectureListResponseDto; +import com.mentionall.cpr2u.education.dto.ScoreRequestDto; import com.mentionall.cpr2u.education.dto.quiz.QuizResponseDto; import com.mentionall.cpr2u.education.service.EducationProgressService; import com.mentionall.cpr2u.education.service.LectureService; @@ -26,6 +25,8 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import java.time.LocalDateTime; + import static com.mentionall.cpr2u.util.exception.ResponseCode.OK_CERTIFICATED; import static com.mentionall.cpr2u.util.exception.ResponseCode.OK_SUCCESS; @@ -49,7 +50,7 @@ public class EducationController { "is_posture_completed : μ‚¬μš©μžμ˜ μžμ„Έ μ‹€μŠ΅ μ™„λ£Œ μ—¬λΆ€(0: λ―Έμ™„ / 2: μ™„λ£Œ)") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성곡", - content = @Content(array = @ArraySchema(schema = @Schema(implementation = EducationProgressDto.class)))), + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ProgressResponseDto.class)))), }) @GetMapping() public ResponseEntity getEducationInfo(@GetUserDetails PrincipalDetails userDetails) { @@ -62,14 +63,14 @@ public ResponseEntity getEducationInfo(@GetUserDetails Pri @Operation(summary = "μœ μ €μ˜ κ°•μ˜ 리슀트 쑰회", description = "κ°•μ˜ λ¦¬μŠ€νŠΈμ™€ μœ μ €μ˜ ν˜„μž¬ κ°•μ˜ 진도λ₯Ό μ‘°νšŒν•œλ‹€.") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성곡", - content = @Content(array = @ArraySchema(schema = @Schema(implementation = LectureProgressDto.class)))), + content = @Content(array = @ArraySchema(schema = @Schema(implementation = LectureListResponseDto.class)))), }) @GetMapping("/lectures") public ResponseEntity getLectureList(@GetUserDetails PrincipalDetails userDetails) { return ResponseDataTemplate.toResponseEntity( OK_SUCCESS, - lectureService.readLectureProgress(userDetails.getUser())); + lectureService.readLectureProgressAndList(userDetails.getUser())); } @Operation(summary = "κ°•μ˜ μˆ˜κ°• μ™„λ£Œ", description = "μœ μ €κ°€ λ§ˆμ§€λ§‰μœΌλ‘œ μ™„λ£Œν•œ κ°•μ˜λ₯Ό lectureId κ°’μ˜ κ°•μ˜λ‘œ λ³€κ²½ν•œλ‹€.") @@ -107,24 +108,12 @@ public ResponseEntity getQuizList() { }) @PostMapping("/quizzes/progress") public ResponseEntity completeQuiz( - @Parameter(description = "μœ μ €μ˜ 점수") @RequestBody ScoreDto requestDto, + @Parameter(description = "μœ μ €μ˜ 점수") @RequestBody ScoreRequestDto requestDto, @GetUserDetails PrincipalDetails userDetails) { progressService.completeQuiz(userDetails.getUser(), requestDto); return ResponseTemplate.toResponseEntity(OK_SUCCESS); } - @Operation(summary = "μžμ„Έμ‹€μŠ΅ κ°•μ˜ 쑰회", description = "μžμ„Έμ‹€μŠ΅ κ°•μ˜ μ˜μƒ URLλ₯Ό μ‘°νšŒν•œλ‹€.") - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "성곡", - content = @Content(array = @ArraySchema(schema = @Schema(implementation = PostureLectureResponseDto.class)))), - }) - @GetMapping("/exercises") - public ResponseEntity getPostureLecture() { - return ResponseDataTemplate.toResponseEntity( - OK_SUCCESS, - lectureService.readPostureLecture()); - } - @Operation(summary = "μžμ„Έμ‹€μŠ΅ ν…ŒμŠ€νŠΈ μ™„λ£Œ", description = "μœ μ €κ°€ μžμ„Έμ‹€μŠ΅ ν…ŒμŠ€νŠΈλ₯Ό ν†΅κ³Όν–ˆμŒμ„ μ €μž₯ν•œλ‹€.") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성곡", @@ -135,9 +124,9 @@ public ResponseEntity getPostureLecture() { @PostMapping("/exercises/progress") public ResponseEntity completePosture( @GetUserDetails PrincipalDetails userDetails, - @Parameter(description = "μœ μ €μ˜ 점수") @RequestBody ScoreDto requestDto) { + @Parameter(description = "μœ μ €μ˜ 점수") @RequestBody ScoreRequestDto requestDto) { progressService.completePosture(userDetails.getUser(), requestDto); - userService.certificate(userDetails.getUser()); + userService.certificate(userDetails.getUser(), LocalDateTime.now()); return ResponseTemplate.toResponseEntity(OK_CERTIFICATED); } diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/domain/EducationProgress.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/domain/EducationProgress.java deleted file mode 100644 index 3f520be..0000000 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/domain/EducationProgress.java +++ /dev/null @@ -1,80 +0,0 @@ -package com.mentionall.cpr2u.education.domain; - -import com.mentionall.cpr2u.user.domain.User; -import com.mentionall.cpr2u.util.Timestamped; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import javax.persistence.*; -import java.util.ArrayList; - -@Entity -@Getter -@NoArgsConstructor -@AllArgsConstructor -public class EducationProgress extends Timestamped { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @OneToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "user_id") - private User user; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "lecture_id") - private Lecture lastLecture = null; - - @Column - private int quizScore = 0; - - @Column - private int postureScore = 0; - - public EducationProgress(User user) { - this.user = user; - } - - public Lecture getLastLecture() { - if (this.lastLecture == null) - return new Lecture(0L, "", "", 0, "", new ArrayList()); - return this.lastLecture; - } - - public double getTotalProgress() { - int currentProgress = this.getLastLecture().getStep(); - if (this.quizScore >= TestStandard.quizScore) currentProgress++; - if (this.postureScore >= TestStandard.postureScore) currentProgress++; - - return (double)currentProgress / (double)TestStandard.totalStep; - } - - public ProgressStatus getLectureProgressStatus() { - if (this.getLastLecture().getStep() == 0) return ProgressStatus.NotCompleted; - if (this.getLastLecture().getStep() == TestStandard.finalLectureStep) return ProgressStatus.Completed; - return ProgressStatus.InProgress; - } - - public ProgressStatus getQuizProgressStatus() { - if (this.quizScore >= TestStandard.quizScore) return ProgressStatus.Completed; - return ProgressStatus.NotCompleted; - } - - public ProgressStatus getPostureProgressStatus() { - if (this.postureScore >= TestStandard.postureScore) return ProgressStatus.Completed; - return ProgressStatus.NotCompleted; - } - - public void updateQuizScore(int score) { - this.quizScore = score; - } - - public void updatePostureScore(int score) { - this.postureScore = score; - } - - public void updateLecture(Lecture lecture) { - this.lastLecture = lecture; - } -} diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/domain/Lecture.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/domain/Lecture.java index 3b93038..6bae9f6 100644 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/domain/Lecture.java +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/domain/Lecture.java @@ -1,5 +1,6 @@ package com.mentionall.cpr2u.education.domain; +import com.mentionall.cpr2u.education.domain.progress.EducationProgress; import com.mentionall.cpr2u.education.dto.lecture.LectureRequestDto; import lombok.AllArgsConstructor; import lombok.Getter; @@ -30,7 +31,7 @@ public class Lecture implements Comparable { @Column(length = 50) private String description; - @OneToMany(cascade = CascadeType.ALL, mappedBy = "lastLecture") + @OneToMany(cascade = CascadeType.ALL, mappedBy = "lectureProgress.lastLecture") List progressList = new ArrayList(); public Lecture(LectureRequestDto requestDto) { diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/domain/ProgressStatus.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/domain/ProgressStatus.java index 582f3cf..6de4154 100644 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/domain/ProgressStatus.java +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/domain/ProgressStatus.java @@ -9,6 +9,4 @@ public enum ProgressStatus { Completed("Completed"); private final String status; - public static final int lastLectureStep = 4; - public static final int totalStep = 6; } diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/domain/TestStandard.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/domain/TestStandard.java index 7cebf6e..6799dbe 100644 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/domain/TestStandard.java +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/domain/TestStandard.java @@ -4,9 +4,12 @@ @Data public class TestStandard { - public static int finalLectureStep = 1; - public static int totalStep = 3; + public static final int finalLectureStep = 1; + public static final int totalStep = 3; + + public static final int quizScore = 100; + public static final int postureScore = 80; + public static final long validTime = 90L; + - public static int quizScore = 100; - public static int postureScore = 80; } diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/domain/progress/EducationProgress.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/domain/progress/EducationProgress.java new file mode 100644 index 0000000..9427d3b --- /dev/null +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/domain/progress/EducationProgress.java @@ -0,0 +1,68 @@ +package com.mentionall.cpr2u.education.domain.progress; + +import com.mentionall.cpr2u.education.domain.TestStandard; +import com.mentionall.cpr2u.user.domain.User; +import com.mentionall.cpr2u.util.Timestamped; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import javax.persistence.*; + +import static com.mentionall.cpr2u.education.domain.ProgressStatus.*; + +@Entity +@Getter +@NoArgsConstructor +@AllArgsConstructor +public class EducationProgress extends Timestamped { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @OneToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "user_id") + private User user; + + @Embedded + @AssociationOverride(name = "lastLecture", joinColumns = @JoinColumn(name = "last_lecture_id")) + @AttributeOverrides({ + @AttributeOverride(name = "status", column = @Column(name = "lecture_status")) + }) + private LectureProgress lectureProgress; + + @Embedded + @AttributeOverrides({ + @AttributeOverride(name = "status", column = @Column(name = "quiz_status")), + @AttributeOverride(name = "score", column = @Column(name = "quiz_score")) + }) + private QuizProgress quizProgress; + + @Embedded + @AttributeOverrides({ + @AttributeOverride(name = "status", column = @Column(name = "posture_status")), + @AttributeOverride(name = "score", column = @Column(name = "posture_score")) + }) + private PostureProgress postureProgress; + + public EducationProgress(User user) { + this.user = user; + this.lectureProgress = new LectureProgress(); + this.quizProgress = new QuizProgress(); + this.postureProgress = new PostureProgress(); + } + + public double getTotalProgress() { + int currentProgress = this.lectureProgress.getLastStep(); + if (this.quizProgress.getStatus() == Completed) currentProgress++; + if (this.postureProgress.getStatus() == Completed) currentProgress++; + + return (double)currentProgress / (double)TestStandard.totalStep; + } + + public void reset() { + this.lectureProgress = new LectureProgress(); + this.quizProgress = new QuizProgress(); + this.postureProgress = new PostureProgress(); + } +} diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/domain/progress/LectureProgress.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/domain/progress/LectureProgress.java new file mode 100644 index 0000000..9c92da9 --- /dev/null +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/domain/progress/LectureProgress.java @@ -0,0 +1,37 @@ +package com.mentionall.cpr2u.education.domain.progress; + +import com.mentionall.cpr2u.education.domain.Lecture; +import com.mentionall.cpr2u.education.domain.ProgressStatus; +import com.mentionall.cpr2u.education.domain.TestStandard; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import javax.persistence.Embeddable; +import javax.persistence.FetchType; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; + +import static com.mentionall.cpr2u.education.domain.ProgressStatus.Completed; +import static com.mentionall.cpr2u.education.domain.ProgressStatus.InProgress; + +@Embeddable +@NoArgsConstructor +public class LectureProgress { + private ProgressStatus status = ProgressStatus.NotCompleted; + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "last_lecture_id") + private Lecture lastLecture = null; + + public ProgressStatus getStatus() { + return this.status; + } + + public int getLastStep() { + return (lastLecture == null) ? 0 : lastLecture.getStep(); + } + + public void updateLastLecture(Lecture lecture) { + this.lastLecture = lecture; + status = (lecture.getStep() >= TestStandard.finalLectureStep) ? Completed : InProgress; + } +} diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/domain/progress/PostureProgress.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/domain/progress/PostureProgress.java new file mode 100644 index 0000000..4030f51 --- /dev/null +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/domain/progress/PostureProgress.java @@ -0,0 +1,23 @@ +package com.mentionall.cpr2u.education.domain.progress; + +import com.mentionall.cpr2u.education.domain.ProgressStatus; +import com.mentionall.cpr2u.education.domain.TestStandard; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import javax.persistence.Embeddable; + +import static com.mentionall.cpr2u.education.domain.ProgressStatus.Completed; + +@Embeddable +@Getter +@NoArgsConstructor +public class PostureProgress { + private ProgressStatus status = ProgressStatus.NotCompleted; + private int score = 0; + + public void updateScore(int score) { + this.score = score; + if (score >= TestStandard.postureScore) status = Completed; + } +} diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/domain/progress/QuizProgress.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/domain/progress/QuizProgress.java new file mode 100644 index 0000000..5a0103a --- /dev/null +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/domain/progress/QuizProgress.java @@ -0,0 +1,23 @@ +package com.mentionall.cpr2u.education.domain.progress; + +import com.mentionall.cpr2u.education.domain.ProgressStatus; +import com.mentionall.cpr2u.education.domain.TestStandard; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import javax.persistence.Embeddable; + +import static com.mentionall.cpr2u.education.domain.ProgressStatus.*; + +@Embeddable +@Getter +@NoArgsConstructor +public class QuizProgress { + private ProgressStatus status = NotCompleted; + private int score = 0; + + public void updateScore(int score) { + this.score = score; + if (score >= TestStandard.quizScore) status = Completed; + } +} diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/dto/EducationProgressDto.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/dto/ProgressResponseDto.java similarity index 54% rename from CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/dto/EducationProgressDto.java rename to CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/dto/ProgressResponseDto.java index b1cbb59..ca2306d 100644 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/dto/EducationProgressDto.java +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/dto/ProgressResponseDto.java @@ -1,17 +1,18 @@ package com.mentionall.cpr2u.education.dto; import com.fasterxml.jackson.annotation.JsonProperty; -import com.mentionall.cpr2u.education.domain.EducationProgress; -import com.mentionall.cpr2u.user.domain.AngelStatusEnum; -import com.mentionall.cpr2u.user.domain.User; +import com.mentionall.cpr2u.education.domain.progress.EducationProgress; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import java.time.LocalDate; import java.time.temporal.ChronoUnit; +import static com.mentionall.cpr2u.education.domain.TestStandard.validTime; +import static com.mentionall.cpr2u.user.domain.AngelStatus.*; + @Data -public class EducationProgressDto { +public class ProgressResponseDto { @Schema(example = "μ‚¬μš©μžμ˜ μ—”μ € μƒνƒœ(0: 수료 / 1: 만료 / 2: 미수료)") @JsonProperty("angel_status") private int angelStatus; @@ -28,10 +29,6 @@ public class EducationProgressDto { @JsonProperty("is_lecture_completed") private int isLectureCompleted; - @Schema(example = "λ§ˆμ§€λ§‰μœΌλ‘œ 이수λ₯Ό μ™„λ£Œν•œ κ°•μ˜λͺ…(λ¬΄μ‹œν•΄μ£Όμ„Έμš”.)") - @JsonProperty("last_lecture_title") - private String lastLectureTitle; - @Schema(example = "μ‚¬μš©μžμ˜ ν€΄μ¦ˆ μ™„λ£Œ μ—¬λΆ€(0: λ―Έμ™„ / 2: μ™„λ£Œ)") @JsonProperty("is_quiz_completed") private int isQuizCompleted; @@ -44,18 +41,21 @@ public class EducationProgressDto { @JsonProperty("days_left_until_expiration") private Integer daysLeftUntilExpiration; - public EducationProgressDto(EducationProgress progress, User user) { - this.angelStatus = user.getStatus().ordinal(); - this.nickname = user.getNickname(); + public ProgressResponseDto(EducationProgress progress) { + this.angelStatus = progress.getUser().getAngelStatus().ordinal(); + this.nickname = progress.getUser().getNickname(); this.progressPercent = progress.getTotalProgress(); - this.lastLectureTitle = progress.getLastLecture().getTitle(); - this.isLectureCompleted = progress.getLectureProgressStatus().ordinal(); - this.isQuizCompleted = progress.getQuizProgressStatus().ordinal(); - this.isPostureCompleted = progress.getPostureProgressStatus().ordinal(); - if(this.angelStatus != AngelStatusEnum.UNACQUIRED.ordinal()) { - int leftDays = 90 + (int)(ChronoUnit.DAYS.between(LocalDate.now(), user.getDateOfIssue().toLocalDate().atStartOfDay())); - this.daysLeftUntilExpiration = leftDays >= 0 ? leftDays : null; + + this.isLectureCompleted = progress.getLectureProgress().getStatus().ordinal(); + this.isQuizCompleted = progress.getQuizProgress().getStatus().ordinal(); + this.isPostureCompleted = progress.getPostureProgress().getStatus().ordinal(); + + if(progress.getUser().getAngelStatus() == UNACQUIRED) { + this.daysLeftUntilExpiration = null; + } else { + LocalDate issuedAt = progress.getUser().getCertificate().getDateOfIssue().toLocalDate(); + long leftDays = validTime - (ChronoUnit.DAYS.between(issuedAt, LocalDate.now())); + this.daysLeftUntilExpiration = leftDays >= 0 ? (int)leftDays : null; } - else this.daysLeftUntilExpiration = null; } } diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/dto/ScoreDto.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/dto/ScoreRequestDto.java similarity index 92% rename from CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/dto/ScoreDto.java rename to CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/dto/ScoreRequestDto.java index 0db33db..230047c 100644 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/dto/ScoreDto.java +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/dto/ScoreRequestDto.java @@ -9,7 +9,7 @@ @Data @AllArgsConstructor @NoArgsConstructor -public class ScoreDto { +public class ScoreRequestDto { @Schema(example = "점수(1 ~ 100)") @JsonProperty diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/dto/LectureProgressDto.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/dto/lecture/LectureListResponseDto.java similarity index 53% rename from CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/dto/LectureProgressDto.java rename to CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/dto/lecture/LectureListResponseDto.java index a292f9f..25a8b05 100644 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/dto/LectureProgressDto.java +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/dto/lecture/LectureListResponseDto.java @@ -1,14 +1,12 @@ -package com.mentionall.cpr2u.education.dto; +package com.mentionall.cpr2u.education.dto.lecture; import com.fasterxml.jackson.annotation.JsonProperty; -import com.mentionall.cpr2u.education.domain.EducationProgress; -import com.mentionall.cpr2u.education.dto.lecture.LectureResponseDto; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import java.util.List; @Data -public class LectureProgressDto { +public class LectureListResponseDto { @Schema(example = "λ§ˆμ§€λ§‰μœΌλ‘œ 이수 μ™„λ£Œν•œ κ°•μ˜ μ„Ήμ…˜(1~4)") @JsonProperty("current_step") private int currentStep; @@ -16,8 +14,8 @@ public class LectureProgressDto { @JsonProperty("lecture_list") private List lectureList; - public LectureProgressDto(EducationProgress progress, List lectureList) { - this.currentStep = progress.getLastLecture().getStep(); + public LectureListResponseDto(int currentStep, List lectureList) { + this.currentStep = currentStep; this.lectureList = lectureList; } } diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/dto/lecture/PostureLectureResponseDto.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/dto/lecture/PostureLectureResponseDto.java deleted file mode 100644 index 5012e61..0000000 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/dto/lecture/PostureLectureResponseDto.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.mentionall.cpr2u.education.dto.lecture; - -import com.fasterxml.jackson.annotation.JsonProperty; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.AllArgsConstructor; -import lombok.Data; - -@Data -@AllArgsConstructor -public class PostureLectureResponseDto { - @Schema(example = "κ°•μ˜ μ˜μƒ URL") - @JsonProperty("video_url") - String videoUrl; -} diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/repository/EducationProgressRepository.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/repository/EducationProgressRepository.java index d5ec110..a389634 100644 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/repository/EducationProgressRepository.java +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/repository/EducationProgressRepository.java @@ -1,6 +1,6 @@ package com.mentionall.cpr2u.education.repository; -import com.mentionall.cpr2u.education.domain.EducationProgress; +import com.mentionall.cpr2u.education.domain.progress.EducationProgress; import com.mentionall.cpr2u.user.domain.User; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/service/EducationProgressService.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/service/EducationProgressService.java index 6d1a0f7..cd7276b 100644 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/service/EducationProgressService.java +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/service/EducationProgressService.java @@ -1,11 +1,11 @@ package com.mentionall.cpr2u.education.service; -import com.mentionall.cpr2u.education.domain.EducationProgress; +import com.mentionall.cpr2u.education.domain.progress.EducationProgress; import com.mentionall.cpr2u.education.domain.Lecture; import com.mentionall.cpr2u.education.domain.ProgressStatus; import com.mentionall.cpr2u.education.domain.TestStandard; -import com.mentionall.cpr2u.education.dto.EducationProgressDto; -import com.mentionall.cpr2u.education.dto.ScoreDto; +import com.mentionall.cpr2u.education.dto.ProgressResponseDto; +import com.mentionall.cpr2u.education.dto.ScoreRequestDto; import com.mentionall.cpr2u.education.repository.EducationProgressRepository; import com.mentionall.cpr2u.education.repository.LectureRepository; import com.mentionall.cpr2u.user.domain.User; @@ -14,57 +14,73 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import javax.transaction.Transactional; + +import static com.mentionall.cpr2u.education.domain.ProgressStatus.*; + @Service @RequiredArgsConstructor public class EducationProgressService { private final EducationProgressRepository progressRepository; private final LectureRepository lectureRepository; - public void completeQuiz(User user, ScoreDto requestDto) { - EducationProgress progress = getEducationProgressByUser(user); + @Transactional + public void completeQuiz(User user, ScoreRequestDto requestDto) { + EducationProgress progress = getEducationProgress(user); - if (progress.getLectureProgressStatus() != ProgressStatus.Completed) + if (!checkPossibleToTakeQuiz(progress)) throw new CustomException(ResponseCode.BAD_REQUEST_EDUCATION_PERMISSION_DENIED); - progress.updateQuizScore(requestDto.getScore()); + progress.getQuizProgress().updateScore(requestDto.getScore()); progressRepository.save(progress); if (requestDto.getScore() < TestStandard.quizScore) throw new CustomException(ResponseCode.OK_QUIZ_FAIL); } - public void completePosture(User user, ScoreDto requestDto) { - EducationProgress progress = getEducationProgressByUser(user); + @Transactional + public void completePosture(User user, ScoreRequestDto requestDto) { + EducationProgress progress = getEducationProgress(user); - if (progress.getLectureProgressStatus() != ProgressStatus.Completed || - progress.getQuizProgressStatus() != ProgressStatus.Completed) + if (!checkPossibleToTakePractice(progress)) throw new CustomException(ResponseCode.BAD_REQUEST_EDUCATION_PERMISSION_DENIED); - progress.updatePostureScore(requestDto.getScore()); + progress.getPostureProgress().updateScore(requestDto.getScore()); progressRepository.save(progress); - if (progress.getPostureScore() < TestStandard.postureScore) + if (progress.getPostureProgress().getScore() < TestStandard.postureScore) throw new CustomException(ResponseCode.OK_POSTURE_FAIL); } - public EducationProgressDto readEducationInfo(User user) { - EducationProgress progress = getEducationProgressByUser(user); - - return new EducationProgressDto(progress, user); + @Transactional + public ProgressResponseDto readEducationInfo(User user) { + return new ProgressResponseDto(getEducationProgress(user)); } + + @Transactional public void completeLecture(User user, Long lectureId) { - EducationProgress progress = getEducationProgressByUser(user); + EducationProgress progress = getEducationProgress(user); + Lecture lecture = lectureRepository.findById(lectureId).orElseThrow( () -> new CustomException(ResponseCode.SERVER_ERROR_FAILED_TO_FIND_LECTURE) ); - progress.updateLecture(lecture); + + progress.getLectureProgress().updateLastLecture(lecture); progressRepository.save(progress); } - private EducationProgress getEducationProgressByUser(User user) { + private EducationProgress getEducationProgress(User user) { return progressRepository.findByUser(user).orElseThrow( () -> new CustomException(ResponseCode.SERVER_ERROR_FAILED_TO_GET_EDUCATION_PROGRESS) ); } + private boolean checkPossibleToTakeQuiz(EducationProgress progress) { + return progress.getLectureProgress().getStatus() == Completed; + } + + private boolean checkPossibleToTakePractice(EducationProgress progress) { + return progress.getLectureProgress().getStatus() == Completed && + progress.getQuizProgress().getStatus() == Completed; + } } diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/service/LectureService.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/service/LectureService.java index 89b1bf0..58f8df4 100644 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/service/LectureService.java +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/service/LectureService.java @@ -1,18 +1,16 @@ package com.mentionall.cpr2u.education.service; -import com.mentionall.cpr2u.education.domain.EducationProgress; import com.mentionall.cpr2u.education.domain.Lecture; -import com.mentionall.cpr2u.education.dto.LectureProgressDto; +import com.mentionall.cpr2u.education.domain.progress.EducationProgress; +import com.mentionall.cpr2u.education.dto.lecture.LectureListResponseDto; import com.mentionall.cpr2u.education.dto.lecture.LectureRequestDto; import com.mentionall.cpr2u.education.dto.lecture.LectureResponseDto; -import com.mentionall.cpr2u.education.dto.lecture.PostureLectureResponseDto; import com.mentionall.cpr2u.education.repository.EducationProgressRepository; import com.mentionall.cpr2u.education.repository.LectureRepository; import com.mentionall.cpr2u.user.domain.User; import com.mentionall.cpr2u.util.exception.CustomException; import com.mentionall.cpr2u.util.exception.ResponseCode; import lombok.RequiredArgsConstructor; -import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import java.util.List; @@ -24,35 +22,23 @@ public class LectureService { private final LectureRepository lectureRepository; private final EducationProgressRepository progressRepository; - @Value("${lecture.posture-url}") - private String postureUrl; - public void createLecture(LectureRequestDto requestDto) { - if (lectureRepository.existsByStep(requestDto.getStep())) - throw new CustomException(ResponseCode.BAD_REQUEST_LECTURE_DUPLICATED); - lectureRepository.save(new Lecture(requestDto)); } - public LectureProgressDto readLectureProgress(User user) { + public LectureListResponseDto readLectureProgressAndList(User user) { EducationProgress progress = progressRepository.findByUser(user).orElseThrow( () -> new CustomException(ResponseCode.SERVER_ERROR_FAILED_TO_GET_EDUCATION_PROGRESS) ); - List lectureResponseDtoList = lectureRepository.findAll() + List lectureList = lectureRepository.findAll() .stream().sorted() .map(l -> new LectureResponseDto(l)) .collect(Collectors.toList()); - return new LectureProgressDto(progress, lectureResponseDtoList); - } - - public List readAllTheoryLecture() { - return lectureRepository.findAll().stream().sorted() - .map(l -> new LectureResponseDto(l)) - .collect(Collectors.toList()); - } - public PostureLectureResponseDto readPostureLecture() { - return new PostureLectureResponseDto(postureUrl); + return new LectureListResponseDto( + progress.getLectureProgress().getLastStep(), + lectureList + ); } } diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/service/QuizService.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/service/QuizService.java index 76c6df7..79251d0 100644 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/service/QuizService.java +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/education/service/QuizService.java @@ -15,6 +15,8 @@ import javax.transaction.Transactional; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; import static com.mentionall.cpr2u.util.exception.ResponseCode.BAD_REQUEST_QUIZ_WRONG_ANSWER; import static com.mentionall.cpr2u.util.exception.ResponseCode.NOT_FOUND_QUIZ; @@ -42,12 +44,10 @@ public void createQuiz(QuizRequestDto requestDto) { @Transactional public List readRandom5Quiz() { - List quizList = quizRepository.findRandomLimit5(); - List response = new ArrayList<>(); - for (int i = 0; i < quizList.size(); i++) { - response.add(new QuizResponseDto(i+1, quizList.get(i))); - } - return response; + AtomicInteger index = new AtomicInteger(1); + return quizRepository.findRandomLimit5().stream() + .map(q -> new QuizResponseDto(index.getAndIncrement(), q)) + .collect(Collectors.toList()); } @Transactional diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/manager/ManagerController.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/manager/ManagerController.java index be453b7..5ba6cda 100644 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/manager/ManagerController.java +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/manager/ManagerController.java @@ -2,6 +2,7 @@ import com.mentionall.cpr2u.education.dto.quiz.QuizRequestDto; import com.mentionall.cpr2u.education.service.QuizService; +import com.mentionall.cpr2u.user.service.AddressService; import com.mentionall.cpr2u.util.ResponseTemplate; import io.swagger.v3.oas.annotations.Hidden; import lombok.RequiredArgsConstructor; @@ -19,6 +20,7 @@ public class ManagerController { private final QuizService quizService; + private final AddressService addressService; @PostMapping("/quizzes") public ResponseEntity createQuiz(@RequestBody QuizRequestDto requestDto) { @@ -32,4 +34,9 @@ public ResponseEntity deleteQuiz(@RequestParam Long id) { return ResponseTemplate.toResponseEntity(OK_SUCCESS); } + @PostMapping("/addresses") + public ResponseEntity loadAllAddresses() { + addressService.loadAddressList(); + return ResponseTemplate.toResponseEntity(OK_SUCCESS); + } } diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/manager/ManagerService.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/manager/ManagerService.java index cc4c9a7..64ff9ee 100644 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/manager/ManagerService.java +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/manager/ManagerService.java @@ -1,56 +1,72 @@ package com.mentionall.cpr2u.manager; import com.mentionall.cpr2u.call.domain.CprCall; -import com.mentionall.cpr2u.call.dto.FcmPushTypeEnum; import com.mentionall.cpr2u.call.repository.CprCallRepository; -import com.mentionall.cpr2u.call.service.FirebaseCloudMessageService; +import com.mentionall.cpr2u.util.fcm.FirebaseCloudMessageUtil; import com.mentionall.cpr2u.user.domain.User; import com.mentionall.cpr2u.user.repository.UserRepository; -import com.mentionall.cpr2u.util.MessageEnum; -import com.mentionall.cpr2u.util.exception.CustomException; -import com.mentionall.cpr2u.util.exception.ResponseCode; +import com.mentionall.cpr2u.util.fcm.FcmDataType; +import com.mentionall.cpr2u.util.fcm.FcmPushType; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; -import java.io.IOException; import java.time.LocalDate; import java.time.temporal.ChronoUnit; +import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; +import static com.mentionall.cpr2u.education.domain.TestStandard.validTime; +import static com.mentionall.cpr2u.user.domain.AngelStatus.ACQUIRED; +import static com.mentionall.cpr2u.util.fcm.FcmMessage.ANGEL_EXPIRED_BODY; +import static com.mentionall.cpr2u.util.fcm.FcmMessage.ANGEL_EXPIRED_TITLE; + @Service +@Slf4j @RequiredArgsConstructor public class ManagerService { private final UserRepository userRepository; private final CprCallRepository cprCallRepository; - private final FirebaseCloudMessageService firebaseCloudMessageService; + private final FirebaseCloudMessageUtil firebaseCloudMessageUtil; @Scheduled(cron = "1 0 0 * * *") public void updateAngelStatus() { - List userList = userRepository.findAllAngel(); - for (User user : userList) { - int leftDays = 90 + (int) (ChronoUnit.DAYS.between(LocalDate.now(), user.getDateOfIssue().toLocalDate().atStartOfDay())); - if (leftDays < 0) { - user.expireCertificate(); - userRepository.save(user); - try { - firebaseCloudMessageService.sendMessageTo(user.getDeviceToken().getToken(), - MessageEnum.ANGEL_EXPIRED_TITLE.getMessage(), - MessageEnum.ANGEL_EXPIRED_BODY.getMessage(), - new LinkedHashMap<>(){{ - put("type", String.valueOf(FcmPushTypeEnum.ANGLE_EXPIRATION.ordinal())); - }}); - } catch (IOException e) { - throw new CustomException(ResponseCode.SERVER_ERROR_FAILED_TO_SEND_FCM); - } + List validAngelList = userRepository.findAllByCertificateStatus(ACQUIRED); + List expiredAngelTokenList = new ArrayList<>(); + + for (User angel : validAngelList) { + LocalDate issuedAt = angel.getCertificate().getDateOfIssue().toLocalDate(); + long currentTime = ChronoUnit.DAYS.between(issuedAt, LocalDate.now()); + + if (currentTime >= validTime) { + angel.expireCertificate(); + userRepository.save(angel); + expiredAngelTokenList.add(angel.getDeviceToken().getToken()); } } + sendExpiredFcm(expiredAngelTokenList); + } + + @Scheduled(cron = "1 0 0 * * *") + public void updateCallStatus() { List callList = cprCallRepository.findAllCallInProgressButExpired(); - for(CprCall cprCall : callList){ + + for (CprCall cprCall : callList) { cprCall.endSituationCprCall(); cprCallRepository.save(cprCall); } } + + private void sendExpiredFcm(List expiredAngelTokenList) { + firebaseCloudMessageUtil.sendFcmMessage( + expiredAngelTokenList, + ANGEL_EXPIRED_TITLE.getMessage(), + ANGEL_EXPIRED_BODY.getMessage(), + new LinkedHashMap<>() {{ + put(FcmDataType.TYPE.getType(), String.valueOf(FcmPushType.ANGLE_EXPIRATION.ordinal())); + }}); + } } diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/controller/AuthController.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/controller/AuthController.java index c7ead21..dfc5e71 100644 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/controller/AuthController.java +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/controller/AuthController.java @@ -1,7 +1,11 @@ package com.mentionall.cpr2u.user.controller; -import com.mentionall.cpr2u.user.dto.*; -import com.mentionall.cpr2u.user.service.UserService; +import com.mentionall.cpr2u.user.domain.PrincipalDetails; +import com.mentionall.cpr2u.user.dto.address.AddressResponseDto; +import com.mentionall.cpr2u.user.dto.user.*; +import com.mentionall.cpr2u.user.service.AddressService; +import com.mentionall.cpr2u.user.service.AuthService; +import com.mentionall.cpr2u.util.GetUserDetails; import com.mentionall.cpr2u.util.ResponseDataTemplate; import com.mentionall.cpr2u.util.ResponseTemplate; import io.swagger.v3.oas.annotations.Operation; @@ -23,7 +27,8 @@ @RequestMapping("/auth") @Tag(name = "AuthController", description = "νšŒμ›κ°€μž…/둜그인") public class AuthController { - private final UserService userService; + private final AuthService authService; + private final AddressService addressService; @Operation(summary = "νšŒμ›κ°€μž…", method = "POST", @@ -31,13 +36,13 @@ public class AuthController { ) @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "νšŒμ›κ°€μž… 성곡", - content = @Content(array = @ArraySchema(schema = @Schema(implementation = UserTokenDto.class)))) + content = @Content(array = @ArraySchema(schema = @Schema(implementation = TokenResponseDto.class)))) }) @PostMapping("/signup") - public ResponseEntity signup(@RequestBody UserSignUpDto userSignUpDto){ + public ResponseEntity signup(@RequestBody SignUpRequestDto signUpRequestDto){ return ResponseDataTemplate.toResponseEntity( OK_SUCCESS, - userService.signup(userSignUpDto) + authService.signup(signUpRequestDto) ); } @@ -47,13 +52,25 @@ public ResponseEntity signup(@RequestBody UserSignUpDto us ) @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "인증번호 λ°œκΈ‰ 성곡", - content = @Content(array = @ArraySchema(schema = @Schema(implementation = UserCodeDto.class)))) + content = @Content(array = @ArraySchema(schema = @Schema(implementation = CodeResponseDto.class)))) }) @PostMapping("/verification") - public ResponseEntity issueVerificationCode(@RequestBody UserPhoneNumberDto userPhoneNumberDto){ + public ResponseEntity issueVerificationCode(@RequestBody PhoneNumberRequestDto userPhoneNumberRequestDto){ return ResponseDataTemplate.toResponseEntity( OK_SUCCESS, - userService.getVerificationCode(userPhoneNumberDto) + authService.getVerificationCode(userPhoneNumberRequestDto) + ); + } + + @Operation(summary = "μ£Όμ†Œ 리슀트 쑰회", description = "μ „κ΅­ μ‹œλ„μ™€ μ‹œκ΅°κ΅¬ μ£Όμ†Œ 리슀트λ₯Ό μ‘°νšŒν•œλ‹€.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성곡", content = @Content(array = @ArraySchema(schema = @Schema(implementation = AddressResponseDto.class)))), + }) + @GetMapping("/address") + public ResponseEntity readAddressList() { + return ResponseDataTemplate.toResponseEntity( + OK_SUCCESS, + addressService.readAll() ); } @@ -63,15 +80,15 @@ public ResponseEntity issueVerificationCode(@RequestBody U ) @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "둜그인 성곡", - content = @Content(array = @ArraySchema(schema = @Schema(implementation = UserTokenDto.class)))), + content = @Content(array = @ArraySchema(schema = @Schema(implementation = TokenResponseDto.class)))), @ApiResponse(responseCode = "404", description = "νšŒμ›κ°€μž…μ΄ ν•„μš”ν•œ μ‚¬μš©μž", content = @Content(array = @ArraySchema(schema = @Schema(implementation = ResponseTemplate.class)))) }) @PostMapping("/login") - public ResponseEntity verificationUserLogin(@RequestBody UserLoginDto userLoginDto){ + public ResponseEntity verificationUserLogin(@RequestBody LoginRequestDto loginRequestDto){ return ResponseDataTemplate.toResponseEntity( OK_SUCCESS, - userService.login(userLoginDto) + authService.login(loginRequestDto) ); } @Operation(summary = "λ‹‰λ„€μž„ 쀑볡확인", @@ -86,7 +103,7 @@ public ResponseEntity verificationUserLogin(@RequestBody U }) @GetMapping("/nickname") public ResponseEntity nicknameCheck(@RequestParam("nickname") String nickname){ - userService.checkNicknameDuplicated(nickname); + authService.checkNicknameDuplicated(nickname); return ResponseTemplate.toResponseEntity(OK_NICKNAME_CHECK); } @@ -97,17 +114,32 @@ public ResponseEntity nicknameCheck(@RequestParam("nickname") ) @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "μžλ™ 둜그인 성곡", - content = @Content(array = @ArraySchema(schema = @Schema(implementation = UserTokenDto.class)))), + content = @Content(array = @ArraySchema(schema = @Schema(implementation = TokenResponseDto.class)))), @ApiResponse(responseCode = "403", description = "μœ νš¨ν•˜μ§€ μ•Šμ€ refresh token", content = @Content(array = @ArraySchema(schema = @Schema(implementation = ResponseTemplate.class)))) }) @PostMapping("/auto-login") - public ResponseEntity autoLogin(@RequestBody UserTokenReissueDto userTokenReissueDto){ + public ResponseEntity autoLogin(@RequestBody TokenReissueRequestDto tokenReissueRequestDto){ return ResponseDataTemplate.toResponseEntity( OK_SUCCESS, - userService.reissueToken(userTokenReissueDto) + authService.reissueToken(tokenReissueRequestDto) ); } + @Operation(summary = "λ‘œκ·Έμ•„μ›ƒ", + method = "POST", + description = "λ‘œκ·Έμ•„μ›ƒ API" + ) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "λ‘œκ·Έμ•„μ›ƒ 성곡", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ResponseTemplate.class)))), + }) + @PostMapping("/logout") + public ResponseEntity logout(@GetUserDetails PrincipalDetails userDetails) { + authService.logout(userDetails.getUser()); + return ResponseTemplate.toResponseEntity( + OK_SUCCESS + ); + } } diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/controller/UserController.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/controller/UserController.java index c3e4169..2c2b233 100644 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/controller/UserController.java +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/controller/UserController.java @@ -1,8 +1,8 @@ package com.mentionall.cpr2u.user.controller; import com.mentionall.cpr2u.user.domain.PrincipalDetails; -import com.mentionall.cpr2u.user.dto.AddressRequestDto; -import com.mentionall.cpr2u.user.dto.AddressResponseDto; +import com.mentionall.cpr2u.user.dto.address.AddressRequestDto; +import com.mentionall.cpr2u.user.dto.address.AddressResponseDto; import com.mentionall.cpr2u.user.service.AddressService; import com.mentionall.cpr2u.util.GetUserDetails; import com.mentionall.cpr2u.util.ResponseDataTemplate; @@ -28,18 +28,6 @@ public class UserController { private final AddressService addressService; - @Operation(summary = "μ£Όμ†Œ 리슀트 쑰회", description = "μ „κ΅­ μ‹œλ„μ™€ μ‹œκ΅°κ΅¬ μ£Όμ†Œ 리슀트λ₯Ό μ‘°νšŒν•œλ‹€.") - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "성곡", content = @Content(array = @ArraySchema(schema = @Schema(implementation = AddressResponseDto.class)))), - }) - @GetMapping("/address") - public ResponseEntity readAddressList() { - return ResponseDataTemplate.toResponseEntity( - OK_SUCCESS, - addressService.readAll() - ); - } - @Operation(summary = "μ‚¬μš©μž μ£Όμ†Œ μ„€μ •", description = "μ‚¬μš©μžμ˜ μ£Όμ†Œλ₯Ό νŒŒλΌλ―Έν„°λ‘œ λ„˜μ–΄μ˜¨ ID의 μ£Όμ†Œλ‘œ μ„€μ •ν•œλ‹€.") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성곡", content = @Content(array = @ArraySchema(schema = @Schema(implementation = ResponseTemplate.class)))), diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/domain/AngelStatusEnum.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/domain/AngelStatus.java similarity index 89% rename from CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/domain/AngelStatusEnum.java rename to CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/domain/AngelStatus.java index 8312669..8fa7b74 100644 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/domain/AngelStatusEnum.java +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/domain/AngelStatus.java @@ -5,7 +5,7 @@ @RequiredArgsConstructor @Getter -public enum AngelStatusEnum { +public enum AngelStatus { ACQUIRED("ACQUIRED"), EXPIRED("EXPIRED"), diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/domain/Certificate.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/domain/Certificate.java new file mode 100644 index 0000000..6eeaba2 --- /dev/null +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/domain/Certificate.java @@ -0,0 +1,29 @@ +package com.mentionall.cpr2u.user.domain; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import javax.persistence.Embeddable; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import java.time.LocalDateTime; + +@Getter +@Embeddable +@NoArgsConstructor +@AllArgsConstructor +public class Certificate { + @Enumerated(EnumType.STRING) + private AngelStatus status; + private LocalDateTime dateOfIssue; + + public void acquire(LocalDateTime dateOfIssue) { + this.status = AngelStatus.ACQUIRED; + this.dateOfIssue = dateOfIssue; + } + + public void expire() { + this.status = AngelStatus.EXPIRED; + } +} diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/domain/DeviceToken.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/domain/DeviceToken.java index edcc561..1955480 100644 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/domain/DeviceToken.java +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/domain/DeviceToken.java @@ -26,6 +26,10 @@ public DeviceToken(String deviceToken, User user) { this.token = deviceToken; } + public DeviceToken(User user) { + this.user = user; + } + public void setToken(String deviceToken) { this.token = deviceToken; } diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/domain/FcmToken.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/domain/FcmToken.java index 85730b1..fc0e053 100644 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/domain/FcmToken.java +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/domain/FcmToken.java @@ -26,7 +26,7 @@ public FcmToken(String fcmToken, User user) { this.token = fcmToken; } - public void setToken(String deviceToken) { - this.token = deviceToken; + public void setToken(String fcmToken) { + this.token = fcmToken; } } diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/domain/User.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/domain/User.java index de1bf45..2ff6a6e 100644 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/domain/User.java +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/domain/User.java @@ -2,8 +2,8 @@ import com.mentionall.cpr2u.call.domain.Dispatch; import com.mentionall.cpr2u.call.domain.Report; -import com.mentionall.cpr2u.education.domain.EducationProgress; -import com.mentionall.cpr2u.user.dto.UserSignUpDto; +import com.mentionall.cpr2u.education.domain.progress.EducationProgress; +import com.mentionall.cpr2u.user.dto.user.SignUpRequestDto; import com.mentionall.cpr2u.util.RandomGenerator; import com.mentionall.cpr2u.util.Timestamped; import lombok.AllArgsConstructor; @@ -37,27 +37,23 @@ public class User extends Timestamped{ @Column(length = 20, unique = true) private String phoneNumber; - @Column - private LocalDateTime dateOfIssue; + @Embedded + private Certificate certificate; - @Column(length = 10) - @Enumerated(EnumType.STRING) - private AngelStatusEnum status; - - @OneToOne(mappedBy = "user") + @OneToOne(mappedBy = "user", cascade = CascadeType.ALL) private EducationProgress educationProgress; - @OneToOne(mappedBy = "user") + @OneToOne(mappedBy = "user", cascade = CascadeType.REMOVE) private RefreshToken refreshToken; - @OneToOne(mappedBy = "user") + @OneToOne(mappedBy = "user", cascade = CascadeType.REMOVE) private DeviceToken deviceToken; @ElementCollection(fetch = FetchType.LAZY) private List roles = new ArrayList<>(); @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "address_id") + @JoinColumn(name = "address_id", nullable = false) private Address address; @OneToMany(cascade = CascadeType.ALL, mappedBy = "dispatcher") @@ -66,28 +62,39 @@ public class User extends Timestamped{ @OneToMany(cascade = CascadeType.ALL, mappedBy = "reporter") List reportList = new ArrayList(); - public User(UserSignUpDto userSignUpDto) { - this.nickname = userSignUpDto.getNickname(); - this.phoneNumber = userSignUpDto.getPhoneNumber(); - this.dateOfIssue = null; - this.status = AngelStatusEnum.UNACQUIRED; + public User(SignUpRequestDto requestDto, Address address) { + this.nickname = requestDto.getNickname(); + this.phoneNumber = requestDto.getPhoneNumber(); + this.address = address; + this.certificate = new Certificate(AngelStatus.UNACQUIRED, null); this.roles.add(UserRole.USER); } + public AngelStatus getAngelStatus() { + return this.certificate.getStatus(); + } + public void setDeviceToken(DeviceToken deviceToken) { this.deviceToken = deviceToken; } + public void setRefreshToken(RefreshToken refreshToken) { + this.refreshToken = refreshToken; + } public void setAddress(Address address) { this.address = address; } - public void acquireCertification() { - this.status = AngelStatusEnum.ACQUIRED; - this.dateOfIssue = LocalDateTime.now(); + public void setEducationProgress(EducationProgress progress) { + this.educationProgress = progress; + } + + public void acquireCertification(LocalDateTime dateOfIssue) { + this.certificate.acquire(dateOfIssue); } public void expireCertificate() { - this.status = AngelStatusEnum.EXPIRED; + this.certificate.expire(); + this.educationProgress.reset(); } } diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/UserNicknameDto.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/UserNicknameDto.java deleted file mode 100644 index d8f0b13..0000000 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/UserNicknameDto.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.mentionall.cpr2u.user.dto; - -import com.fasterxml.jackson.annotation.JsonProperty; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -import javax.validation.constraints.NotBlank; - -@Data -@AllArgsConstructor -@NoArgsConstructor -public class UserNicknameDto { - - @Schema(example = "μ‚¬μš©μž 이름") - @JsonProperty("nickname") - @NotBlank(message = "이름이 μž…λ ₯λ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€.") - String nickname; - -} diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/AddressRequestDto.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/address/AddressRequestDto.java similarity index 88% rename from CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/AddressRequestDto.java rename to CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/address/AddressRequestDto.java index 84df5b4..a5a13bd 100644 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/AddressRequestDto.java +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/address/AddressRequestDto.java @@ -1,4 +1,4 @@ -package com.mentionall.cpr2u.user.dto; +package com.mentionall.cpr2u.user.dto.address; import com.fasterxml.jackson.annotation.JsonProperty; import io.swagger.v3.oas.annotations.media.Schema; diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/AddressResponseDto.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/address/AddressResponseDto.java similarity index 90% rename from CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/AddressResponseDto.java rename to CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/address/AddressResponseDto.java index 5239acc..e45d34d 100644 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/AddressResponseDto.java +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/address/AddressResponseDto.java @@ -1,8 +1,7 @@ -package com.mentionall.cpr2u.user.dto; +package com.mentionall.cpr2u.user.dto.address; import com.fasterxml.jackson.annotation.JsonProperty; import io.swagger.v3.oas.annotations.media.Schema; -import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/SigugunResponseDto.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/address/SigugunResponseDto.java similarity index 87% rename from CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/SigugunResponseDto.java rename to CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/address/SigugunResponseDto.java index cbc8753..96b0191 100644 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/SigugunResponseDto.java +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/address/SigugunResponseDto.java @@ -1,4 +1,4 @@ -package com.mentionall.cpr2u.user.dto; +package com.mentionall.cpr2u.user.dto.address; import io.swagger.v3.oas.annotations.media.Schema; import lombok.AllArgsConstructor; diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/UserCodeDto.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/user/CodeResponseDto.java similarity index 79% rename from CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/UserCodeDto.java rename to CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/user/CodeResponseDto.java index 58feec9..0cd88cd 100644 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/UserCodeDto.java +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/user/CodeResponseDto.java @@ -1,4 +1,4 @@ -package com.mentionall.cpr2u.user.dto; +package com.mentionall.cpr2u.user.dto.user; import com.fasterxml.jackson.annotation.JsonProperty; import io.swagger.v3.oas.annotations.media.Schema; @@ -7,7 +7,7 @@ @Data @AllArgsConstructor -public class UserCodeDto { +public class CodeResponseDto { @Schema(example = "validation code") @JsonProperty("validation_code") diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/UserLoginDto.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/user/LoginRequestDto.java similarity index 85% rename from CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/UserLoginDto.java rename to CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/user/LoginRequestDto.java index b1a582e..bed6253 100644 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/UserLoginDto.java +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/user/LoginRequestDto.java @@ -1,4 +1,4 @@ -package com.mentionall.cpr2u.user.dto; +package com.mentionall.cpr2u.user.dto.user; import com.fasterxml.jackson.annotation.JsonProperty; import io.swagger.v3.oas.annotations.media.Schema; @@ -9,7 +9,7 @@ @Data @AllArgsConstructor @NoArgsConstructor -public class UserLoginDto { +public class LoginRequestDto { @Schema(example = "μ‚¬μš©μž μ „ν™”λ²ˆν˜Έ") @JsonProperty("phone_number") diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/UserPhoneNumberDto.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/user/PhoneNumberRequestDto.java similarity index 81% rename from CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/UserPhoneNumberDto.java rename to CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/user/PhoneNumberRequestDto.java index 42c3880..c9ba7de 100644 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/UserPhoneNumberDto.java +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/user/PhoneNumberRequestDto.java @@ -1,4 +1,4 @@ -package com.mentionall.cpr2u.user.dto; +package com.mentionall.cpr2u.user.dto.user; import com.fasterxml.jackson.annotation.JsonProperty; import io.swagger.v3.oas.annotations.media.Schema; @@ -9,7 +9,7 @@ @Data @AllArgsConstructor @NoArgsConstructor -public class UserPhoneNumberDto { +public class PhoneNumberRequestDto { @Schema(example = "μ‚¬μš©μž μ „ν™”λ²ˆν˜Έ") @JsonProperty("phone_number") diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/UserSignUpDto.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/user/SignUpRequestDto.java similarity index 74% rename from CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/UserSignUpDto.java rename to CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/user/SignUpRequestDto.java index 5284436..5e26f11 100644 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/UserSignUpDto.java +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/user/SignUpRequestDto.java @@ -1,4 +1,4 @@ -package com.mentionall.cpr2u.user.dto; +package com.mentionall.cpr2u.user.dto.user; import com.fasterxml.jackson.annotation.JsonProperty; import io.swagger.v3.oas.annotations.media.Schema; @@ -9,7 +9,7 @@ @Data @AllArgsConstructor @NoArgsConstructor -public class UserSignUpDto { +public class SignUpRequestDto { @Schema(example = "μ‚¬μš©μž 이름") @JsonProperty("nickname") @@ -19,6 +19,10 @@ public class UserSignUpDto { @JsonProperty("phone_number") String phoneNumber; + @Schema(example = "μ‚¬μš©μž μ£Όμ†Œμ§€ ID") + @JsonProperty("address_id") + private Long addressId; + @Schema(example = "device token") @JsonProperty("device_token") String deviceToken; diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/UserTokenReissueDto.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/user/TokenReissueRequestDto.java similarity index 80% rename from CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/UserTokenReissueDto.java rename to CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/user/TokenReissueRequestDto.java index 28d9205..0505256 100644 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/UserTokenReissueDto.java +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/user/TokenReissueRequestDto.java @@ -1,4 +1,4 @@ -package com.mentionall.cpr2u.user.dto; +package com.mentionall.cpr2u.user.dto.user; import com.fasterxml.jackson.annotation.JsonProperty; import io.swagger.v3.oas.annotations.media.Schema; @@ -9,7 +9,7 @@ @Data @AllArgsConstructor @NoArgsConstructor -public class UserTokenReissueDto { +public class TokenReissueRequestDto { @Schema(example = "refresh token") @JsonProperty("refresh_token") diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/UserTokenDto.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/user/TokenResponseDto.java similarity index 83% rename from CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/UserTokenDto.java rename to CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/user/TokenResponseDto.java index 96b58c5..38c2862 100644 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/UserTokenDto.java +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/dto/user/TokenResponseDto.java @@ -1,4 +1,4 @@ -package com.mentionall.cpr2u.user.dto; +package com.mentionall.cpr2u.user.dto.user; import com.fasterxml.jackson.annotation.JsonProperty; import io.swagger.v3.oas.annotations.media.Schema; @@ -7,7 +7,7 @@ @Data @AllArgsConstructor -public class UserTokenDto { +public class TokenResponseDto { @Schema(example = "access token") @JsonProperty("access_token") diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/repository/AddressDslRepository.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/repository/AddressDslRepository.java deleted file mode 100644 index b36613f..0000000 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/repository/AddressDslRepository.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.mentionall.cpr2u.user.repository; - -import com.mentionall.cpr2u.user.domain.Address; - -import java.util.Optional; - -public interface AddressDslRepository { - Optional

findByFullAddress(String[] addressList); -} diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/repository/DeviceTokenDslRepository.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/repository/DeviceTokenDslRepository.java deleted file mode 100644 index bcd5479..0000000 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/repository/DeviceTokenDslRepository.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.mentionall.cpr2u.user.repository; - -import com.mentionall.cpr2u.user.domain.DeviceToken; - -import java.util.List; - -public interface DeviceTokenDslRepository { - List findAllDeviceTokenByUserAddress(Long addressId, String userId); -} diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/repository/DeviceTokenRepositoryImpl.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/repository/DeviceTokenRepositoryImpl.java deleted file mode 100644 index 8b43451..0000000 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/repository/DeviceTokenRepositoryImpl.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.mentionall.cpr2u.user.repository; - -import com.mentionall.cpr2u.user.domain.DeviceToken; -import com.mentionall.cpr2u.user.domain.QDeviceToken; -import com.querydsl.jpa.impl.JPAQueryFactory; - -import javax.persistence.EntityManager; -import java.util.List; - -import static com.mentionall.cpr2u.user.domain.QDeviceToken.*; - -public class DeviceTokenRepositoryImpl implements DeviceTokenDslRepository { - - private final JPAQueryFactory queryFactory; - - public DeviceTokenRepositoryImpl(EntityManager em) { - this.queryFactory = new JPAQueryFactory(em); - } - - @Override - public List findAllDeviceTokenByUserAddress(Long addressId, String userId) { - return queryFactory.selectFrom(deviceToken) - .where(deviceToken.user.address.id.eq(addressId) - .and(deviceToken.user.id.ne(userId))) - .fetch(); - } -} diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/repository/UserRepository.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/repository/UserRepository.java index 05af207..7c21976 100644 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/repository/UserRepository.java +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/repository/UserRepository.java @@ -1,8 +1,8 @@ package com.mentionall.cpr2u.user.repository; +import com.mentionall.cpr2u.user.domain.AngelStatus; import com.mentionall.cpr2u.user.domain.User; import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Query; import java.util.List; import java.util.Optional; @@ -10,10 +10,7 @@ public interface UserRepository extends JpaRepository{ Optional findByPhoneNumber(String phoneNumber); - Boolean existsByPhoneNumber(String phoneNumber); + List findAllByCertificateStatus(AngelStatus status); Boolean existsByNickname(String nickname); - - @Query("SELECT u FROM User u WHERE u.status = 'ACQUIRED'") - List findAllAngel(); } diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/repository/address/AddressDslRepository.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/repository/address/AddressDslRepository.java new file mode 100644 index 0000000..820e103 --- /dev/null +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/repository/address/AddressDslRepository.java @@ -0,0 +1,12 @@ +package com.mentionall.cpr2u.user.repository.address; + +import com.mentionall.cpr2u.user.domain.Address; + +import java.util.List; +import java.util.Optional; + +public interface AddressDslRepository { + List findAllSido(); + + Optional
findByFullAddress(String fullAddress); +} diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/repository/AddressRepository.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/repository/address/AddressRepository.java similarity index 71% rename from CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/repository/AddressRepository.java rename to CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/repository/address/AddressRepository.java index 13c1c00..d8f568f 100644 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/repository/AddressRepository.java +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/repository/address/AddressRepository.java @@ -1,4 +1,4 @@ -package com.mentionall.cpr2u.user.repository; +package com.mentionall.cpr2u.user.repository.address; import com.mentionall.cpr2u.user.domain.Address; import org.springframework.data.jpa.repository.JpaRepository; @@ -9,9 +9,4 @@ import java.util.List; @Repository -public interface AddressRepository extends JpaRepository , AddressDslRepository, QuerydslPredicateExecutor
{ - - // TODO: Querydsl둜 λ¦¬νŒ©ν† λ§ - @Query("SELECT DISTINCT a.sido FROM Address a") - List findAllSido(); -} +public interface AddressRepository extends JpaRepository , AddressDslRepository, QuerydslPredicateExecutor
{ } diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/repository/AddressRepositoryImpl.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/repository/address/AddressRepositoryImpl.java similarity index 72% rename from CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/repository/AddressRepositoryImpl.java rename to CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/repository/address/AddressRepositoryImpl.java index 6ed74c2..81d13fe 100644 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/repository/AddressRepositoryImpl.java +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/repository/address/AddressRepositoryImpl.java @@ -1,8 +1,9 @@ -package com.mentionall.cpr2u.user.repository; +package com.mentionall.cpr2u.user.repository.address; import com.mentionall.cpr2u.user.domain.Address; import com.querydsl.jpa.impl.JPAQuery; import com.querydsl.jpa.impl.JPAQueryFactory; +import lombok.extern.slf4j.Slf4j; import javax.persistence.EntityManager; import java.util.List; @@ -11,6 +12,7 @@ import static com.mentionall.cpr2u.user.domain.QAddress.address; +@Slf4j public class AddressRepositoryImpl implements AddressDslRepository { private final JPAQueryFactory queryFactory; @@ -20,11 +22,20 @@ public AddressRepositoryImpl(EntityManager em) { } @Override - public Optional
findByFullAddress(String[] addressList) { + public List findAllSido(){ + return queryFactory.select(address.sido) + .from(address) + .distinct().fetch(); + } + + @Override + public Optional
findByFullAddress(String fullAddress) { + String[] addressList = fullAddress.split(" "); JPAQuery
findAddressQuery = queryFactory.selectFrom(address).where(address.sido.contains(addressList[0])); List
findAddressList = findAddressQuery.fetch(); - for(int i = 1 ; findAddressList.size() > 1 && i <= 2; i ++) { + + for(int i = 1; findAddressList.size() > 1 && i <= 2; i++) { String sigugun = addressList[i]; findAddressQuery = findBySigugunQuery(findAddressQuery, sigugun); findAddressList = findAddressQuery.fetch(); diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/repository/device_token/DeviceTokenDslRepository.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/repository/device_token/DeviceTokenDslRepository.java new file mode 100644 index 0000000..54634be --- /dev/null +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/repository/device_token/DeviceTokenDslRepository.java @@ -0,0 +1,9 @@ +package com.mentionall.cpr2u.user.repository.device_token; + +import org.springframework.data.domain.Pageable; + +import java.util.List; + +public interface DeviceTokenDslRepository { + List findAllDeviceTokenByUserAddressExceptCaller(Long addressId, String userId, Pageable pageable); +} diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/repository/DeviceTokenRepository.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/repository/device_token/DeviceTokenRepository.java similarity index 87% rename from CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/repository/DeviceTokenRepository.java rename to CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/repository/device_token/DeviceTokenRepository.java index e22e6a7..194ae71 100644 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/repository/DeviceTokenRepository.java +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/repository/device_token/DeviceTokenRepository.java @@ -1,4 +1,4 @@ -package com.mentionall.cpr2u.user.repository; +package com.mentionall.cpr2u.user.repository.device_token; import com.mentionall.cpr2u.user.domain.DeviceToken; import org.springframework.data.jpa.repository.JpaRepository; diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/repository/device_token/DeviceTokenRepositoryImpl.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/repository/device_token/DeviceTokenRepositoryImpl.java new file mode 100644 index 0000000..29d09ff --- /dev/null +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/repository/device_token/DeviceTokenRepositoryImpl.java @@ -0,0 +1,31 @@ +package com.mentionall.cpr2u.user.repository.device_token; + +import com.mentionall.cpr2u.user.domain.AngelStatus; +import com.querydsl.jpa.impl.JPAQueryFactory; +import org.springframework.data.domain.Pageable; + +import javax.persistence.EntityManager; +import java.util.List; + +import static com.mentionall.cpr2u.user.domain.QDeviceToken.deviceToken; + +public class DeviceTokenRepositoryImpl implements DeviceTokenDslRepository { + + private final JPAQueryFactory queryFactory; + + public DeviceTokenRepositoryImpl(EntityManager em) { + this.queryFactory = new JPAQueryFactory(em); + } + + @Override + public List findAllDeviceTokenByUserAddressExceptCaller(Long addressId, String userId, Pageable pageable) { + return queryFactory.select(deviceToken.token) + .from(deviceToken) + .where(deviceToken.user.address.id.eq(addressId) + .and(deviceToken.user.id.ne(userId)) + .and(deviceToken.user.certificate.status.eq(AngelStatus.ACQUIRED))) + .offset(pageable.getOffset()) + .limit(pageable.getPageSize()) + .fetch(); + } +} diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/service/AddressService.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/service/AddressService.java index f34a334..5755ab4 100644 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/service/AddressService.java +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/service/AddressService.java @@ -2,26 +2,56 @@ import com.mentionall.cpr2u.user.domain.Address; import com.mentionall.cpr2u.user.domain.User; -import com.mentionall.cpr2u.user.dto.AddressRequestDto; -import com.mentionall.cpr2u.user.dto.AddressResponseDto; -import com.mentionall.cpr2u.user.dto.SigugunResponseDto; -import com.mentionall.cpr2u.user.repository.AddressRepository; +import com.mentionall.cpr2u.user.dto.address.AddressRequestDto; +import com.mentionall.cpr2u.user.dto.address.AddressResponseDto; +import com.mentionall.cpr2u.user.dto.address.SigugunResponseDto; +import com.mentionall.cpr2u.user.repository.address.AddressRepository; import com.mentionall.cpr2u.user.repository.UserRepository; +import com.mentionall.cpr2u.util.CsvFileParser; import com.mentionall.cpr2u.util.exception.CustomException; import com.mentionall.cpr2u.util.exception.ResponseCode; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.core.io.ClassPathResource; import org.springframework.stereotype.Service; +import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; +import static com.mentionall.cpr2u.util.exception.ResponseCode.SERVER_ERROR_PARSING_URI; + +@Slf4j @Service @RequiredArgsConstructor public class AddressService { private final AddressRepository addressRepository; - private final UserRepository userRepository; + private final CsvFileParser csvFileParser; + + private static final String csvFileName = "cpr2u_address_table.csv"; + + public void loadAddressList() { + addressRepository.deleteAll(); + + List> parsingList; + try { + parsingList = csvFileParser.parse(new ClassPathResource(csvFileName).getURI()); + } catch (IOException e) { + e.printStackTrace(); + throw new CustomException(SERVER_ERROR_PARSING_URI); + } + + // remove a header row. + parsingList.remove(0); + + List
addressList = parsingList.stream() + .map(l -> (l.size() > 1) ? new Address(l.get(0), l.get(1)) : new Address(l.get(0), "")) + .collect(Collectors.toList()); + + addressRepository.saveAll(addressList); + } public List readAll() { List
addressList = addressRepository.findAll(); diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/service/AuthService.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/service/AuthService.java new file mode 100644 index 0000000..4092c9b --- /dev/null +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/service/AuthService.java @@ -0,0 +1,135 @@ +package com.mentionall.cpr2u.user.service; + +import com.mentionall.cpr2u.config.security.JwtTokenProvider; +import com.mentionall.cpr2u.education.domain.progress.EducationProgress; +import com.mentionall.cpr2u.education.repository.EducationProgressRepository; +import com.mentionall.cpr2u.user.domain.Address; +import com.mentionall.cpr2u.user.domain.DeviceToken; +import com.mentionall.cpr2u.user.domain.RefreshToken; +import com.mentionall.cpr2u.user.domain.User; +import com.mentionall.cpr2u.user.dto.user.*; +import com.mentionall.cpr2u.user.repository.RefreshTokenRepository; +import com.mentionall.cpr2u.user.repository.UserRepository; +import com.mentionall.cpr2u.user.repository.address.AddressRepository; +import com.mentionall.cpr2u.user.repository.device_token.DeviceTokenRepository; +import com.mentionall.cpr2u.util.exception.CustomException; +import com.mentionall.cpr2u.util.twilio.TwilioUtil; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Optional; + +import static com.mentionall.cpr2u.util.exception.ResponseCode.*; + +@Service +@RequiredArgsConstructor +public class AuthService { + + private final JwtTokenProvider jwtTokenProvider; + private final UserRepository userRepository; + private final RefreshTokenRepository refreshTokenRepository; + private final DeviceTokenRepository deviceTokenRepository; + private final AddressRepository addressRepository; + private final EducationProgressRepository progressRepository; + private final TwilioUtil fakeTwilioUtil; + + @Transactional + public TokenResponseDto signup(SignUpRequestDto requestDto) { + Address address = addressRepository.findById(requestDto.getAddressId()).orElseThrow( + () -> new CustomException(NOT_FOUND_ADDRESS) + ); + + try { + User user = new User(requestDto, address); + userRepository.save(user); + + createDeviceToken(requestDto.getDeviceToken(), user); + createEducationProgress(user); + + return issueUserTokens(user); + } catch (Exception e) { + throw new CustomException(SERVER_ERROR_FAILED_TO_SIGNUP); + } + + } + + public CodeResponseDto getVerificationCode(PhoneNumberRequestDto requestDto) { + String code = fakeTwilioUtil.makeCodeToVerify(); + fakeTwilioUtil.sendSMS(requestDto.getPhoneNumber(), "Your verification code is " + code); + return new CodeResponseDto(code); + } + + @Transactional + public TokenResponseDto login(LoginRequestDto requestDto) { + User user = userRepository.findByPhoneNumber(requestDto.getPhoneNumber()) + .orElseThrow(() -> new CustomException(NOT_FOUND_USER)); + + createDeviceToken(requestDto.getDeviceToken(), user); + + return issueUserTokens(user); + } + + @Transactional + public TokenResponseDto reissueToken(TokenReissueRequestDto requestDto) { + if (!jwtTokenProvider.validateToken(requestDto.getRefreshToken())) + throw new CustomException(FORBIDDEN_TOKEN_NOT_VALID); + + RefreshToken refreshToken = refreshTokenRepository.findRefreshTokenByToken(requestDto.getRefreshToken()) + .orElseThrow(() -> new CustomException(FORBIDDEN_TOKEN_NOT_VALID)); + return issueUserTokens(refreshToken.getUser()); + } + + public void checkNicknameDuplicated(String nickname) { + if (userRepository.existsByNickname(nickname)) + throw new CustomException(BAD_REQUEST_NICKNAME_DUPLICATED); + } + + public void logout(User user) { + refreshTokenRepository.findByUserId(user.getId()) + .ifPresent( + refreshToken -> refreshTokenRepository.delete(refreshToken) + ); + deviceTokenRepository.findByUserId(user.getId()) + .ifPresent( + deviceToken -> deviceTokenRepository.delete(deviceToken) + ); + } + + private TokenResponseDto issueUserTokens(User user) { + return new TokenResponseDto( + jwtTokenProvider.createAccessToken(user), + createRefreshToken(user).getToken()); + } + + private RefreshToken createRefreshToken(User user) { + RefreshToken refreshToken = refreshTokenRepository.findByUserId(user.getId()) + .orElseGet(() -> new RefreshToken(user)); + + refreshToken.setToken(jwtTokenProvider.createRefreshToken(user)); + refreshTokenRepository.save(refreshToken); + + user.setRefreshToken(refreshToken); + userRepository.save(user); + return refreshToken; + } + + private void createDeviceToken(String token, User user) { + DeviceToken deviceToken = deviceTokenRepository.findByUserId(user.getId()) + .orElseGet(() -> new DeviceToken(user)); + + deviceToken.setToken(token); + deviceTokenRepository.save(deviceToken); + + user.setDeviceToken(deviceToken); + userRepository.save(user); + } + + private void createEducationProgress(User user) { + EducationProgress progress = new EducationProgress(user); + progressRepository.save(progress); + + user.setEducationProgress(progress); + userRepository.save(user); + } +} diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/service/UserService.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/service/UserService.java index 10c8e8b..1eb3d53 100644 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/service/UserService.java +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/user/service/UserService.java @@ -1,124 +1,21 @@ package com.mentionall.cpr2u.user.service; -import com.mentionall.cpr2u.config.security.JwtTokenProvider; -import com.mentionall.cpr2u.education.domain.EducationProgress; -import com.mentionall.cpr2u.education.repository.EducationProgressRepository; -import com.mentionall.cpr2u.user.domain.DeviceToken; -import com.mentionall.cpr2u.user.domain.RefreshToken; import com.mentionall.cpr2u.user.domain.User; -import com.mentionall.cpr2u.user.dto.*; -import com.mentionall.cpr2u.user.repository.DeviceTokenRepository; -import com.mentionall.cpr2u.user.repository.RefreshTokenRepository; import com.mentionall.cpr2u.user.repository.UserRepository; -import com.mentionall.cpr2u.util.exception.CustomException; -import com.mentionall.cpr2u.util.exception.ResponseCode; -import com.twilio.Twilio; -import com.twilio.rest.api.v2010.account.Message; -import com.twilio.rest.verify.v2.service.Verification; -import com.twilio.type.PhoneNumber; import lombok.RequiredArgsConstructor; -import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; @Service @RequiredArgsConstructor public class UserService { - private final JwtTokenProvider jwtTokenProvider; private final UserRepository userRepository; - private final EducationProgressRepository progressRepository; - private final RefreshTokenRepository refreshTokenRepository; - private final DeviceTokenRepository deviceTokenRepository; - - @Value("${security.twilio.account-sid}") - private String twilioAccountSid; - - @Value("${security.twilio.auth-token}") - private String twilioAuthToken; - @Value("${security.twilio.service-sid}") - private String twilioServiceSid; - - @Value("${security.twilio.phone-number}") - private String phoneNumber; - - public UserTokenDto signup(UserSignUpDto userSignUpDto) { - User user = new User(userSignUpDto); + public void certificate(User user, LocalDateTime dateTime) { + user.acquireCertification(dateTime); userRepository.save(user); - deviceTokenRepository.save(new DeviceToken(userSignUpDto.getDeviceToken(), user)); - progressRepository.save(new EducationProgress(user)); - return issueUserToken(user); - } - - public UserCodeDto getVerificationCode(UserPhoneNumberDto userPhoneNumberDto) { -// String code = String.format("%04.0f", Math.random() * Math.pow(10, 4)); - String code = "1111"; - -// Twilio.init(twilioAccountSid, twilioAuthToken); -// -// Verification.creator( -// twilioServiceSid, -// userPhoneNumberDto.getPhoneNumber(), -// "sms"); -// -// Message.creator(new PhoneNumber(userPhoneNumberDto.getPhoneNumber()), -// new PhoneNumber(phoneNumber), "Your verification code is " + code).create(); - - return new UserCodeDto(code); - } - - @Transactional - public UserTokenDto login(UserLoginDto userLoginDto) { - if(userRepository.existsByPhoneNumber(userLoginDto.getPhoneNumber())){ - User user = userRepository.findByPhoneNumber(userLoginDto.getPhoneNumber()) - .orElseThrow(() -> new CustomException(ResponseCode.NOT_FOUND_USER)); - - DeviceToken deviceToken = deviceTokenRepository.findByUserId(user.getId()) - .orElseGet(()->new DeviceToken(userLoginDto.getDeviceToken(), user)); - - if(!deviceToken.getToken().equals(userLoginDto.getDeviceToken())) { - deviceToken.setToken(userLoginDto.getDeviceToken()); - deviceTokenRepository.save(deviceToken); - user.setDeviceToken(deviceToken); - userRepository.save(user); - } - - return issueUserToken(user); - } - throw new CustomException(ResponseCode.NOT_FOUND_USER); - } - - @Transactional - public UserTokenDto reissueToken(UserTokenReissueDto userTokenReissueDto) { - RefreshToken refreshToken; - if(jwtTokenProvider.validateToken(userTokenReissueDto.getRefreshToken())) - refreshToken = refreshTokenRepository.findRefreshTokenByToken(userTokenReissueDto.getRefreshToken()) - .orElseThrow(()-> new CustomException(ResponseCode.FORBIDDEN_TOKEN_NOT_VALID)); - else throw new CustomException(ResponseCode.FORBIDDEN_TOKEN_NOT_VALID); - - User user = refreshToken.getUser(); - return issueUserToken(user); - } - - private UserTokenDto issueUserToken(User user){ - String newRefreshToken = jwtTokenProvider.createRefreshToken(); - RefreshToken refreshToken = refreshTokenRepository.findByUserId(user.getId()).orElseGet(() -> new RefreshToken(user)); - refreshToken.setToken(newRefreshToken); - refreshTokenRepository.save(refreshToken); - - return new UserTokenDto( - jwtTokenProvider.createToken(user.getId(), user.getRoles()), - newRefreshToken); - } - - public void checkNicknameDuplicated(String nickname) { - if(userRepository.existsByNickname(nickname)) - throw new CustomException(ResponseCode.BAD_REQUEST_NICKNAME_DUPLICATED); } - public void certificate(User user) { - user.acquireCertification(); - userRepository.save(user); - } } diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/util/CsvFileParser.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/util/CsvFileParser.java new file mode 100644 index 0000000..b01c551 --- /dev/null +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/util/CsvFileParser.java @@ -0,0 +1,42 @@ +package com.mentionall.cpr2u.util; + +import com.mentionall.cpr2u.util.exception.CustomException; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.io.BufferedReader; +import java.io.IOException; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static com.mentionall.cpr2u.util.exception.ResponseCode.SERVER_ERROR_PARSING_FAILED; + +@Slf4j +@Component +public class CsvFileParser { + + public List> parse(URI uri) { + log.info("Parsing " + uri.getPath() + "..."); + + try { + List> response = new ArrayList<>(); + BufferedReader br = Files.newBufferedReader(Paths.get(uri)); + + String line = ""; + while ((line = br.readLine()) != null) { + String[] words = line.split(","); + response.add(Arrays.asList(words)); + } + + return response; + } catch (IOException e) { + e.printStackTrace(); + throw new CustomException(SERVER_ERROR_PARSING_FAILED); + } + } + +} diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/util/exception/ResponseCode.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/util/exception/ResponseCode.java index 9da65ab..73460a1 100644 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/util/exception/ResponseCode.java +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/util/exception/ResponseCode.java @@ -17,6 +17,7 @@ public enum ResponseCode { OK_CPR_CALL_END_SITUATION(OK, "호좜이 μ’…λ£Œλ˜μ—ˆμŠ΅λ‹ˆλ‹€."), BAD_REQUEST_NICKNAME_DUPLICATED(BAD_REQUEST, "μ€‘λ³΅λœ λ‹‰λ„€μž„ μž…λ‹ˆλ‹€."), + BAD_REQUEST_NOT_VALID_DISPATCH(BAD_REQUEST, "이미 μ’…λ£Œλ˜μ—ˆκ±°λ‚˜, 본인이 μƒμ„±ν•œ ν˜ΈμΆœμ— λŒ€ν•œ μΆœλ™μž…λ‹ˆλ‹€."), BAD_REQUEST_LECTURE_DUPLICATED(BAD_REQUEST, "μ€‘λΆλ˜λŠ” μ„Ήμ…˜μ˜ κ°•μ˜κ°€ μ‘΄μž¬ν•©λ‹ˆλ‹€."), BAD_REQUEST_QUIZ_WRONG_ANSWER(BAD_REQUEST, "ν•΄λ‹Ή ν€΄μ¦ˆμ˜ λ‹΅μ•ˆ μΈλ±μŠ€κ°€ 잘λͺ»λœ 값을 κ°–κ³  μžˆμŠ΅λ‹ˆλ‹€."), BAD_REQUEST_EDUCATION_PERMISSION_DENIED(BAD_REQUEST, "이전 진도λ₯Ό λͺ¨λ‘ μ™„λ£Œν•΄μ•Ό μˆ˜κ°•ν•  수 μžˆμŠ΅λ‹ˆλ‹€."), @@ -32,8 +33,11 @@ public enum ResponseCode { NOT_FOUND_FAILED_TO_MATCH_ADDRESS(INTERNAL_SERVER_ERROR, "ν•΄λ‹Ή μ£Όμ†Œμ§€μ— λ§žλŠ” μ£Όμ†Œ 지역ꡬλ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€."), SERVER_ERROR_FAILED_TO_SEND_FCM(INTERNAL_SERVER_ERROR, "FCM ν‘Έμ‹œ μ•Œλ¦Όμ„ λ³΄λ‚΄λŠ” 데 μ‹€νŒ¨ν–ˆμŠ΅λ‹ˆλ‹€."), + SERVER_ERROR_FAILED_TO_SIGNUP(INTERNAL_SERVER_ERROR, "νšŒμ›κ°€μž…μ— μ‹€νŒ¨ν–ˆμŠ΅λ‹ˆλ‹€."), SERVER_ERROR_FAILED_TO_FIND_LECTURE(INTERNAL_SERVER_ERROR, "ν•΄λ‹Ή κ°•μ˜λ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€."), - SERVER_ERROR_FAILED_TO_GET_EDUCATION_PROGRESS(INTERNAL_SERVER_ERROR, "μœ μ €μ˜ ꡐ윑 진도 정보가 μ‘°νšŒλ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€."); + SERVER_ERROR_FAILED_TO_GET_EDUCATION_PROGRESS(INTERNAL_SERVER_ERROR, "μœ μ €μ˜ ꡐ윑 진도 정보가 μ‘°νšŒλ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€."), + SERVER_ERROR_PARSING_FAILED(INTERNAL_SERVER_ERROR, "CSV νŒŒμ‹±μ„ μ‹€νŒ¨ν–ˆμŠ΅λ‹ˆλ‹€."), + SERVER_ERROR_PARSING_URI(INTERNAL_SERVER_ERROR, "ν•΄λ‹Ή μ΄λ¦„μ˜ λ¦¬μ†ŒμŠ€ 파일이 μ—†μŠ΅λ‹ˆλ‹€."); private final HttpStatus httpStatus; diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/util/fcm/FcmDataType.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/util/fcm/FcmDataType.java new file mode 100644 index 0000000..2442882 --- /dev/null +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/util/fcm/FcmDataType.java @@ -0,0 +1,16 @@ +package com.mentionall.cpr2u.util.fcm; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +public enum FcmDataType { + + TITLE("title"), + BODY("body"), + TYPE("type"), + CPR_CALL_ID("call"); + + private final String type; +} diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/util/MessageEnum.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/util/fcm/FcmMessage.java similarity index 85% rename from CPR2U-Server/src/main/java/com/mentionall/cpr2u/util/MessageEnum.java rename to CPR2U-Server/src/main/java/com/mentionall/cpr2u/util/fcm/FcmMessage.java index f14db8f..5dd915d 100644 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/util/MessageEnum.java +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/util/fcm/FcmMessage.java @@ -1,11 +1,11 @@ -package com.mentionall.cpr2u.util; +package com.mentionall.cpr2u.util.fcm; import lombok.Getter; import lombok.RequiredArgsConstructor; @Getter @RequiredArgsConstructor -public enum MessageEnum { +public enum FcmMessage { ANGEL_EXPIRED_TITLE("CPR Angel κΆŒν•œμ΄ λ§Œλ£Œλ˜μ—ˆμŠ΅λ‹ˆλ‹€."), ANGEL_EXPIRED_BODY("CPR2U에 μ ‘μ†ν•΄μ„œ ꡐ윑 λ°›κ³  Angel κΆŒν•œμ„ μ—°μž₯ν•˜μ„Έμš”."), CPR_CALL_TITLE("CPR Angel의 μΆœλ™μ΄ ν•„μš”ν•©λ‹ˆλ‹€."); diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/dto/FcmPushTypeEnum.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/util/fcm/FcmPushType.java similarity index 73% rename from CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/dto/FcmPushTypeEnum.java rename to CPR2U-Server/src/main/java/com/mentionall/cpr2u/util/fcm/FcmPushType.java index c4e5793..915e7ef 100644 --- a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/call/dto/FcmPushTypeEnum.java +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/util/fcm/FcmPushType.java @@ -1,11 +1,11 @@ -package com.mentionall.cpr2u.call.dto; +package com.mentionall.cpr2u.util.fcm; import lombok.AllArgsConstructor; import lombok.Getter; @AllArgsConstructor @Getter -public enum FcmPushTypeEnum { +public enum FcmPushType { ANGLE_EXPIRATION("ANGEL_EXPIRATION"), CPR_CALL("CPR_CALL"); diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/util/fcm/FirebaseCloudMessageUtil.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/util/fcm/FirebaseCloudMessageUtil.java new file mode 100644 index 0000000..85a5ab3 --- /dev/null +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/util/fcm/FirebaseCloudMessageUtil.java @@ -0,0 +1,82 @@ +package com.mentionall.cpr2u.util.fcm; + +import com.google.auth.oauth2.GoogleCredentials; +import com.google.firebase.FirebaseApp; +import com.google.firebase.FirebaseOptions; +import com.google.firebase.messaging.*; +import com.mentionall.cpr2u.util.exception.CustomException; +import com.mentionall.cpr2u.util.exception.ResponseCode; +import com.mentionall.cpr2u.util.fcm.FcmDataType; +import lombok.RequiredArgsConstructor; +import org.jetbrains.annotations.NotNull; +import org.springframework.core.io.ClassPathResource; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import java.io.IOException; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +@Component +@RequiredArgsConstructor +public class FirebaseCloudMessageUtil { + public static FirebaseApp firebaseApp; + public static final String firebaseAppName = "CPR2U"; + + @PostConstruct + private void initialFirebaseApp() throws IOException { + String firebaseConfigPath = "firebase/firebase_service_key.json"; + FirebaseOptions options = FirebaseOptions.builder() + .setCredentials(GoogleCredentials.fromStream(new ClassPathResource(firebaseConfigPath).getInputStream())) + .build(); + + firebaseApp = FirebaseApp.initializeApp(options, firebaseAppName); + } + + public void sendFcmMessage(List deviceTokenToSendList, String title, String body, Map data) { + + if (deviceTokenToSendList.size() > 0) { + MulticastMessage message = MulticastMessage.builder() + .addAllTokens(deviceTokenToSendList) + .setAndroidConfig(AndroidConfig.builder() + .putAllData(creatDataForAOS(title, body, data)) + .build()) + .setApnsConfig(ApnsConfig.builder() + .putAllCustomData(createDataForiOS(data)) + .setAps(Aps.builder() + .setContentAvailable(true) + .setAlert(ApsAlert.builder() + .setTitle(title) + .setBody(body) + .build()) + .build()) + .build()) + .build(); + + try { + FirebaseMessaging.getInstance(firebaseApp).sendMulticast(message); + } catch (FirebaseMessagingException e) { + throw new CustomException(ResponseCode.SERVER_ERROR_FAILED_TO_SEND_FCM); + } + } + } + + @NotNull + private static LinkedHashMap creatDataForAOS(String title, String body, Map data) { + LinkedHashMap dataForAOS = new LinkedHashMap<>() {{ + put(FcmDataType.TITLE.getType(), title); + put(FcmDataType.BODY.getType(), body); + }}; + dataForAOS.putAll(data); + return dataForAOS; + } + + @NotNull + private static LinkedHashMap createDataForiOS(Map data) { + LinkedHashMap dataForiOS = new LinkedHashMap<>(); + dataForiOS.putAll(data); + return dataForiOS; + } + +} \ No newline at end of file diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/util/twilio/FakeTwilioUtil.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/util/twilio/FakeTwilioUtil.java new file mode 100644 index 0000000..fc408c6 --- /dev/null +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/util/twilio/FakeTwilioUtil.java @@ -0,0 +1,15 @@ +package com.mentionall.cpr2u.util.twilio; + +import org.springframework.stereotype.Component; + +@Component +public class FakeTwilioUtil extends TwilioUtil { + @Override + public String makeCodeToVerify() { + return "1111"; + } + + @Override + public void sendSMS(String phoneNumber, String content) { + } +} diff --git a/CPR2U-Server/src/main/java/com/mentionall/cpr2u/util/twilio/TwilioUtil.java b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/util/twilio/TwilioUtil.java new file mode 100644 index 0000000..cc5e1b8 --- /dev/null +++ b/CPR2U-Server/src/main/java/com/mentionall/cpr2u/util/twilio/TwilioUtil.java @@ -0,0 +1,32 @@ +package com.mentionall.cpr2u.util.twilio; + +import com.twilio.Twilio; +import com.twilio.rest.api.v2010.account.Message; +import com.twilio.rest.verify.v2.service.Verification; +import com.twilio.type.PhoneNumber; +import org.springframework.stereotype.Component; + +@Component +public class TwilioUtil { + //@Value("${security.twilio.account-sid}") + private String twilioAccountSid; + //@Value("${security.twilio.auth-token}") + private String twilioAuthToken; + + //@Value("${security.twilio.service-sid}") + private String twilioServiceSid; + + public String makeCodeToVerify(){ + return String.format("%04.0f", Math.random() * Math.pow(10, 4)); + } + public void sendSMS(String phoneNumber, String content) { + Twilio.init(twilioAccountSid, twilioAuthToken); + + Verification.creator( + twilioServiceSid, + phoneNumber, + "sms"); + + Message.creator(new PhoneNumber(phoneNumber), new PhoneNumber(phoneNumber), content).create(); + } +} diff --git a/CPR2U-Server/src/main/resources/application.properties b/CPR2U-Server/src/main/resources/application.properties new file mode 100644 index 0000000..7a97890 --- /dev/null +++ b/CPR2U-Server/src/main/resources/application.properties @@ -0,0 +1,8 @@ +spring.mvc.pathmatch.matching-strategy=ant_path_matcher +spring.profiles.include=local +spring.jpa.hibernate.ddl-auto=none +spring.jpa.properties.hibernate.format_sql=true +spring.config.import=optional:classpath:/value.yml + +logging.level.org.hibernate.sql=debug +logging.level.org.hibernate.type.descriptor.sql=trace \ No newline at end of file diff --git a/CPR2U-Server/src/main/resources/application.yml b/CPR2U-Server/src/main/resources/application.yml deleted file mode 100644 index 7510c02..0000000 --- a/CPR2U-Server/src/main/resources/application.yml +++ /dev/null @@ -1,24 +0,0 @@ -spring: - mvc: - path match: - matching-strategy: ant_path_matcher - profiles: - include: local - jpa: - hibernate: - ddl-auto: update - properties: - hibernate: - format_sql: true - config: - import: - - optional:classpath:/value.yml - -logging: - level: - org: - hibernate: - SQL: debug - type: - descriptor: - sql: trace \ No newline at end of file diff --git a/CPR2U-Server/src/main/resources/cpr2u_address_table.csv b/CPR2U-Server/src/main/resources/cpr2u_address_table.csv new file mode 100644 index 0000000..a7db2ba --- /dev/null +++ b/CPR2U-Server/src/main/resources/cpr2u_address_table.csv @@ -0,0 +1,249 @@ +ο»Ώsido,sigungu +μ„œμšΈνŠΉλ³„μ‹œ,μ’…λ‘œκ΅¬ +μ„œμšΈνŠΉλ³„μ‹œ,쀑ꡬ +μ„œμšΈνŠΉλ³„μ‹œ,μš©μ‚°κ΅¬ +μ„œμšΈνŠΉλ³„μ‹œ,성동ꡬ +μ„œμšΈνŠΉλ³„μ‹œ,광진ꡬ +μ„œμšΈνŠΉλ³„μ‹œ,λ™λŒ€λ¬Έκ΅¬ +μ„œμšΈνŠΉλ³„μ‹œ,μ€‘λž‘κ΅¬ +μ„œμšΈνŠΉλ³„μ‹œ,성뢁ꡬ +μ„œμšΈνŠΉλ³„μ‹œ,강뢁ꡬ +μ„œμšΈνŠΉλ³„μ‹œ,도봉ꡬ +μ„œμšΈνŠΉλ³„μ‹œ,노원ꡬ +μ„œμšΈνŠΉλ³„μ‹œ,은평ꡬ +μ„œμšΈνŠΉλ³„μ‹œ,μ„œλŒ€λ¬Έκ΅¬ +μ„œμšΈνŠΉλ³„μ‹œ,마포ꡬ +μ„œμšΈνŠΉλ³„μ‹œ,μ–‘μ²œκ΅¬ +μ„œμšΈνŠΉλ³„μ‹œ,κ°•μ„œκ΅¬ +μ„œμšΈνŠΉλ³„μ‹œ,ꡬ둜ꡬ +μ„œμšΈνŠΉλ³„μ‹œ,금천ꡬ +μ„œμšΈνŠΉλ³„μ‹œ,μ˜λ“±ν¬κ΅¬ +μ„œμšΈνŠΉλ³„μ‹œ,λ™μž‘κ΅¬ +μ„œμšΈνŠΉλ³„μ‹œ,관악ꡬ +μ„œμšΈνŠΉλ³„μ‹œ,μ„œμ΄ˆκ΅¬ +μ„œμšΈνŠΉλ³„μ‹œ,강남ꡬ +μ„œμšΈνŠΉλ³„μ‹œ,μ†‘νŒŒκ΅¬ +μ„œμšΈνŠΉλ³„μ‹œ,강동ꡬ +λΆ€μ‚°κ΄‘μ—­μ‹œ,쀑ꡬ +λΆ€μ‚°κ΄‘μ—­μ‹œ,μ„œκ΅¬ +λΆ€μ‚°κ΄‘μ—­μ‹œ,동ꡬ +λΆ€μ‚°κ΄‘μ—­μ‹œ,μ˜λ„κ΅¬ +λΆ€μ‚°κ΄‘μ—­μ‹œ,뢀산진ꡬ +λΆ€μ‚°κ΄‘μ—­μ‹œ,λ™λž˜κ΅¬ +λΆ€μ‚°κ΄‘μ—­μ‹œ,남ꡬ +λΆ€μ‚°κ΄‘μ—­μ‹œ,뢁ꡬ +λΆ€μ‚°κ΄‘μ—­μ‹œ,ν•΄μš΄λŒ€κ΅¬ +λΆ€μ‚°κ΄‘μ—­μ‹œ,μ‚¬ν•˜κ΅¬ +λΆ€μ‚°κ΄‘μ—­μ‹œ,κΈˆμ •κ΅¬ +λΆ€μ‚°κ΄‘μ—­μ‹œ,κ°•μ„œκ΅¬ +λΆ€μ‚°κ΄‘μ—­μ‹œ,μ—°μ œκ΅¬ +λΆ€μ‚°κ΄‘μ—­μ‹œ,수영ꡬ +λΆ€μ‚°κ΄‘μ—­μ‹œ,사상ꡬ +λΆ€μ‚°κ΄‘μ—­μ‹œ,κΈ°μž₯κ΅° +λŒ€κ΅¬κ΄‘μ—­μ‹œ,쀑ꡬ +λŒ€κ΅¬κ΄‘μ—­μ‹œ,동ꡬ +λŒ€κ΅¬κ΄‘μ—­μ‹œ,μ„œκ΅¬ +λŒ€κ΅¬κ΄‘μ—­μ‹œ,남ꡬ +λŒ€κ΅¬κ΄‘μ—­μ‹œ,뢁ꡬ +λŒ€κ΅¬κ΄‘μ—­μ‹œ,μˆ˜μ„±κ΅¬ +λŒ€κ΅¬κ΄‘μ—­μ‹œ,λ‹¬μ„œκ΅¬ +λŒ€κ΅¬κ΄‘μ—­μ‹œ,달성ꡰ +μΈμ²œκ΄‘μ—­μ‹œ,쀑ꡬ +μΈμ²œκ΄‘μ—­μ‹œ,동ꡬ +μΈμ²œκ΄‘μ—­μ‹œ,남ꡬ +μΈμ²œκ΄‘μ—­μ‹œ,μ—°μˆ˜κ΅¬ +μΈμ²œκ΄‘μ—­μ‹œ,남동ꡬ +μΈμ²œκ΄‘μ—­μ‹œ,뢀평ꡬ +μΈμ²œκ΄‘μ—­μ‹œ,계양ꡬ +μΈμ²œκ΄‘μ—­μ‹œ,μ„œκ΅¬ +μΈμ²œκ΄‘μ—­μ‹œ,κ°•ν™”κ΅° +μΈμ²œκ΄‘μ—­μ‹œ,μ˜Ήμ§„κ΅° +κ΄‘μ£Όκ΄‘μ—­μ‹œ,동ꡬ +κ΄‘μ£Όκ΄‘μ—­μ‹œ,μ„œκ΅¬ +κ΄‘μ£Όκ΄‘μ—­μ‹œ,남ꡬ +κ΄‘μ£Όκ΄‘μ—­μ‹œ,뢁ꡬ +κ΄‘μ£Όκ΄‘μ—­μ‹œ,광산ꡬ +λŒ€μ „κ΄‘μ—­μ‹œ,동ꡬ +λŒ€μ „κ΄‘μ—­μ‹œ,쀑ꡬ +λŒ€μ „κ΄‘μ—­μ‹œ,μ„œκ΅¬ +λŒ€μ „κ΄‘μ—­μ‹œ,μœ μ„±κ΅¬ +λŒ€μ „κ΄‘μ—­μ‹œ,λŒ€λ•κ΅¬ +μšΈμ‚°κ΄‘μ—­μ‹œ,쀑ꡬ +μšΈμ‚°κ΄‘μ—­μ‹œ,남ꡬ +μšΈμ‚°κ΄‘μ—­μ‹œ,동ꡬ +μšΈμ‚°κ΄‘μ—­μ‹œ,뢁ꡬ +μšΈμ‚°κ΄‘μ—­μ‹œ,울주ꡰ +μ„Έμ’…νŠΉλ³„μžμΉ˜μ‹œ, +경기도,μˆ˜μ›μ‹œ μž₯μ•ˆκ΅¬ +경기도,μˆ˜μ›μ‹œ κΆŒμ„ κ΅¬ +경기도,μˆ˜μ›μ‹œ νŒ”λ‹¬κ΅¬ +경기도,μˆ˜μ›μ‹œ μ˜ν†΅κ΅¬ +경기도,μ„±λ‚¨μ‹œ μˆ˜μ •κ΅¬ +경기도,μ„±λ‚¨μ‹œ 쀑원ꡬ +경기도,μ„±λ‚¨μ‹œ 뢄당ꡬ +경기도,μ˜μ •λΆ€μ‹œ +경기도,μ•ˆμ–‘μ‹œ λ§Œμ•ˆκ΅¬ +경기도,μ•ˆμ–‘μ‹œ λ™μ•ˆκ΅¬ +경기도,λΆ€μ²œμ‹œ +경기도,κ΄‘λͺ…μ‹œ +경기도,ν‰νƒμ‹œ +경기도,λ™λ‘μ²œμ‹œ +경기도,μ•ˆμ‚°μ‹œ 상둝ꡬ +경기도,μ•ˆμ‚°μ‹œ 단원ꡬ +경기도,κ³ μ–‘μ‹œ 덕양ꡬ +경기도,κ³ μ–‘μ‹œ 일산동ꡬ +경기도,κ³ μ–‘μ‹œ μΌμ‚°μ„œκ΅¬ +경기도,κ³Όμ²œμ‹œ +경기도,κ΅¬λ¦¬μ‹œ +경기도,λ‚¨μ–‘μ£Όμ‹œ +경기도,μ˜€μ‚°μ‹œ +경기도,μ‹œν₯μ‹œ +경기도,κ΅°ν¬μ‹œ +경기도,μ˜μ™•μ‹œ +경기도,ν•˜λ‚¨μ‹œ +경기도,μš©μΈμ‹œ 처인ꡬ +경기도,μš©μΈμ‹œ κΈ°ν₯ꡬ +경기도,μš©μΈμ‹œ μˆ˜μ§€κ΅¬ +경기도,νŒŒμ£Όμ‹œ +경기도,μ΄μ²œμ‹œ +경기도,μ•ˆμ„±μ‹œ +경기도,κΉ€ν¬μ‹œ +경기도,ν™”μ„±μ‹œ +경기도,κ΄‘μ£Όμ‹œ +경기도,μ–‘μ£Όμ‹œ +경기도,ν¬μ²œμ‹œ +경기도,μ—¬μ£Όμ‹œ +경기도,μ—°μ²œκ΅° +경기도,가평ꡰ +경기도,양평ꡰ +강원도,μΆ˜μ²œμ‹œ +강원도,μ›μ£Όμ‹œ +강원도,κ°•λ¦‰μ‹œ +강원도,λ™ν•΄μ‹œ +강원도,νƒœλ°±μ‹œ +강원도,μ†μ΄ˆμ‹œ +강원도,μ‚Όμ²™μ‹œ +강원도,ν™μ²œκ΅° +강원도,νš‘μ„±κ΅° +강원도,μ˜μ›”κ΅° +강원도,평창ꡰ +강원도,μ •μ„ κ΅° +강원도,철원ꡰ +강원도,ν™”μ²œκ΅° +강원도,양ꡬꡰ +강원도,인제ꡰ +강원도,κ³ μ„±κ΅° +강원도,μ–‘μ–‘κ΅° +좩청뢁도,μ²­μ£Όμ‹œ 상당ꡬ +좩청뢁도,μ²­μ£Όμ‹œ μ„œμ›κ΅¬ +좩청뢁도,μ²­μ£Όμ‹œ ν₯덕ꡬ +좩청뢁도,μ²­μ£Όμ‹œ 청원ꡬ +좩청뢁도,μΆ©μ£Όμ‹œ +좩청뢁도,μ œμ²œμ‹œ +좩청뢁도,보은ꡰ +좩청뢁도,μ˜₯천ꡰ +좩청뢁도,μ˜λ™κ΅° +좩청뢁도,증평ꡰ +좩청뢁도,μ§„μ²œκ΅° +좩청뢁도,κ΄΄μ‚°κ΅° +좩청뢁도,μŒμ„±κ΅° +좩청뢁도,단양ꡰ +좩청남도,μ²œμ•ˆμ‹œ 동남ꡬ +좩청남도,μ²œμ•ˆμ‹œ μ„œλΆκ΅¬ +좩청남도,κ³΅μ£Όμ‹œ +좩청남도,λ³΄λ Ήμ‹œ +좩청남도,μ•„μ‚°μ‹œ +좩청남도,μ„œμ‚°μ‹œ +좩청남도,λ…Όμ‚°μ‹œ +좩청남도,κ³„λ£‘μ‹œ +좩청남도,λ‹Ήμ§„μ‹œ +좩청남도,κΈˆμ‚°κ΅° +좩청남도,λΆ€μ—¬κ΅° +좩청남도,μ„œμ²œκ΅° +좩청남도,μ²­μ–‘κ΅° +좩청남도,홍성ꡰ +좩청남도,μ˜ˆμ‚°κ΅° +좩청남도,νƒœμ•ˆκ΅° +전라뢁도,μ „μ£Όμ‹œ 완산ꡬ +전라뢁도,μ „μ£Όμ‹œ 덕진ꡬ +전라뢁도,κ΅°μ‚°μ‹œ +전라뢁도,μ΅μ‚°μ‹œ +전라뢁도,μ •μμ‹œ +전라뢁도,λ‚¨μ›μ‹œ +전라뢁도,κΉ€μ œμ‹œ +전라뢁도,μ™„μ£Όκ΅° +전라뢁도,μ§„μ•ˆκ΅° +전라뢁도,무주ꡰ +전라뢁도,μž₯수ꡰ +전라뢁도,μž„μ‹€κ΅° +전라뢁도,순창ꡰ +전라뢁도,κ³ μ°½κ΅° +전라뢁도,λΆ€μ•ˆκ΅° +전라남도,λͺ©ν¬μ‹œ +전라남도,μ—¬μˆ˜μ‹œ +전라남도,μˆœμ²œμ‹œ +전라남도,λ‚˜μ£Όμ‹œ +전라남도,κ΄‘μ–‘μ‹œ +전라남도,λ‹΄μ–‘κ΅° +전라남도,곑성ꡰ +전라남도,ꡬ둀ꡰ +전라남도,κ³ ν₯κ΅° +전라남도,보성ꡰ +전라남도,ν™”μˆœκ΅° +전라남도,μž₯ν₯κ΅° +전라남도,강진ꡰ +전라남도,해남ꡰ +전라남도,μ˜μ•”κ΅° +전라남도,λ¬΄μ•ˆκ΅° +전라남도,함평ꡰ +전라남도,μ˜κ΄‘κ΅° +전라남도,μž₯μ„±κ΅° +전라남도,완도ꡰ +전라남도,진도ꡰ +전라남도,μ‹ μ•ˆκ΅° +경상뢁도,ν¬ν•­μ‹œ 남ꡬ +경상뢁도,ν¬ν•­μ‹œ 뢁ꡬ +경상뢁도,κ²½μ£Όμ‹œ +경상뢁도,κΉ€μ²œμ‹œ +경상뢁도,μ•ˆλ™μ‹œ +경상뢁도,κ΅¬λ―Έμ‹œ +경상뢁도,μ˜μ£Όμ‹œ +경상뢁도,μ˜μ²œμ‹œ +경상뢁도,μƒμ£Όμ‹œ +경상뢁도,λ¬Έκ²½μ‹œ +경상뢁도,κ²½μ‚°μ‹œ +경상뢁도,κ΅°μœ„κ΅° +경상뢁도,μ˜μ„±κ΅° +경상뢁도,청솑ꡰ +경상뢁도,μ˜μ–‘κ΅° +경상뢁도,μ˜λ•κ΅° +경상뢁도,청도ꡰ +경상뢁도,κ³ λ Ήκ΅° +경상뢁도,μ„±μ£Όκ΅° +경상뢁도,칠곑ꡰ +경상뢁도,예천ꡰ +경상뢁도,봉화ꡰ +경상뢁도,μšΈμ§„κ΅° +경상뢁도,μšΈλ¦‰κ΅° +경상남도,μ°½μ›μ‹œ 의창ꡬ +경상남도,μ°½μ›μ‹œ 성산ꡬ +경상남도,μ°½μ›μ‹œ λ§ˆμ‚°ν•©ν¬κ΅¬ +경상남도,μ°½μ›μ‹œ λ§ˆμ‚°νšŒμ›κ΅¬ +경상남도,μ°½μ›μ‹œ 진해ꡬ +경상남도,μ§„μ£Όμ‹œ +경상남도,ν†΅μ˜μ‹œ +경상남도,μ‚¬μ²œμ‹œ +경상남도,κΉ€ν•΄μ‹œ +경상남도,λ°€μ–‘μ‹œ +경상남도,κ±°μ œμ‹œ +경상남도,μ–‘μ‚°μ‹œ +경상남도,의령ꡰ +경상남도,ν•¨μ•ˆκ΅° +경상남도,μ°½λ…•κ΅° +경상남도,κ³ μ„±κ΅° +경상남도,남해ꡰ +경상남도,ν•˜λ™κ΅° +경상남도,μ‚°μ²­κ΅° +경상남도,함양ꡰ +경상남도,κ±°μ°½κ΅° +경상남도,ν•©μ²œκ΅° diff --git a/CPR2U-Server/src/test/java/com/mentionall/cpr2u/Cpr2uApplicationTests.java b/CPR2U-Server/src/test/java/com/mentionall/cpr2u/Cpr2uApplicationTests.java index 996fabe..0c14546 100644 --- a/CPR2U-Server/src/test/java/com/mentionall/cpr2u/Cpr2uApplicationTests.java +++ b/CPR2U-Server/src/test/java/com/mentionall/cpr2u/Cpr2uApplicationTests.java @@ -2,8 +2,10 @@ import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.TestPropertySource; -@SpringBootTest +//@SpringBootTest class Cpr2uApplicationTests { @Test diff --git a/CPR2U-Server/src/test/java/com/mentionall/cpr2u/TestConfig.java b/CPR2U-Server/src/test/java/com/mentionall/cpr2u/TestConfig.java new file mode 100644 index 0000000..de5e2b3 --- /dev/null +++ b/CPR2U-Server/src/test/java/com/mentionall/cpr2u/TestConfig.java @@ -0,0 +1,21 @@ +package com.mentionall.cpr2u; + +import com.mentionall.cpr2u.call.util.FakeFirebaseCloudMessageUtil; +import com.mentionall.cpr2u.util.fcm.FirebaseCloudMessageUtil; +import com.mentionall.cpr2u.util.twilio.FakeTwilioUtil; +import com.mentionall.cpr2u.util.twilio.TwilioUtil; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class TestConfig { + @Bean + public FirebaseCloudMessageUtil firebaseCloudMessageUtil() { + return new FakeFirebaseCloudMessageUtil(); + } + + @Bean + public TwilioUtil twilioUtil(){ + return new FakeTwilioUtil(); + } +} diff --git a/CPR2U-Server/src/test/java/com/mentionall/cpr2u/call/repository/FakeCprCallRepository.java b/CPR2U-Server/src/test/java/com/mentionall/cpr2u/call/repository/FakeCprCallRepository.java deleted file mode 100644 index ba51944..0000000 --- a/CPR2U-Server/src/test/java/com/mentionall/cpr2u/call/repository/FakeCprCallRepository.java +++ /dev/null @@ -1,234 +0,0 @@ -package com.mentionall.cpr2u.call.repository; - -import com.mentionall.cpr2u.call.domain.CprCall; -import com.mentionall.cpr2u.call.domain.Dispatch; -import com.mentionall.cpr2u.call.dto.CprCallDto; -import com.querydsl.core.types.OrderSpecifier; -import com.querydsl.core.types.Predicate; -import org.springframework.data.domain.Example; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Sort; -import org.springframework.data.repository.query.FluentQuery; - -import java.util.*; -import java.util.function.Function; - -public class FakeCprCallRepository implements CprCallRepository { - Map map = new HashMap(); - @Override - public List findAllCallInProcessByAddress(Long addressId) { - List cprCallDtoList = new ArrayList<>(); - for(int i = 1; i < map.size() ; i++){ - CprCall cprCall = (CprCall) map.get(Long.valueOf(i)); - if(cprCall.getAddress().getId() == addressId) { - CprCallDto cprCallDto = new CprCallDto(cprCall); - cprCallDtoList.add(cprCallDto); - } - } - return cprCallDtoList; - } - - @Override - public List findAllCallInProgressButExpired() { - return null; - } - - @Override - public List findAll() { - return null; - } - - @Override - public List findAll(Sort sort) { - return null; - } - - @Override - public Page findAll(Pageable pageable) { - return null; - } - - @Override - public List findAllById(Iterable longs) { - return null; - } - - @Override - public long count() { - return 0; - } - - @Override - public void deleteById(Long aLong) { - - } - - @Override - public void delete(CprCall entity) { - - } - - @Override - public void deleteAllById(Iterable longs) { - - } - - @Override - public void deleteAll(Iterable entities) { - - } - - @Override - public void deleteAll() { - - } - - @Override - public S save(S entity) { -// map.put((long)(map.size() + 1), entity); -// System.out.println((map.size() + 1)); - map.put(entity.getId(), entity); - return entity; - } - - @Override - public List saveAll(Iterable entities) { - return null; - } - - @Override - public Optional findById(Long aLong) { - return Optional.of((CprCall) map.get(aLong)); - } - - @Override - public boolean existsById(Long aLong) { - return false; - } - - @Override - public void flush() { - - } - - @Override - public List saveAllAndFlush(Iterable entities) { - return null; - } - - @Override - public void deleteAllInBatch(Iterable entities) { - - } - - @Override - public void deleteAllByIdInBatch(Iterable longs) { - - } - - @Override - public void deleteAllInBatch() { - - } - - @Override - public CprCall getOne(Long aLong) { - return null; - } - - @Override - public CprCall getById(Long aLong) { - return null; - } - - @Override - public CprCall getReferenceById(Long aLong) { - return null; - } - - @Override - public Optional findOne(Example example) { - return Optional.empty(); - } - - @Override - public List findAll(Example example) { - return null; - } - - @Override - public List findAll(Example example, Sort sort) { - return null; - } - - @Override - public Page findAll(Example example, Pageable pageable) { - return null; - } - - @Override - public long count(Example example) { - return 0; - } - - @Override - public boolean exists(Example example) { - return false; - } - - @Override - public R findBy(Example example, Function, R> queryFunction) { - return null; - } - - @Override - public S saveAndFlush(S entity) { - return null; - } - - @Override - public Optional findOne(Predicate predicate) { - return Optional.empty(); - } - - @Override - public Iterable findAll(Predicate predicate) { - return null; - } - - @Override - public Iterable findAll(Predicate predicate, Sort sort) { - return null; - } - - @Override - public Iterable findAll(Predicate predicate, OrderSpecifier... orders) { - return null; - } - - @Override - public Iterable findAll(OrderSpecifier... orders) { - return null; - } - - @Override - public Page findAll(Predicate predicate, Pageable pageable) { - return null; - } - - @Override - public long count(Predicate predicate) { - return 0; - } - - @Override - public boolean exists(Predicate predicate) { - return false; - } - - @Override - public R findBy(Predicate predicate, Function, R> queryFunction) { - return null; - } -} diff --git a/CPR2U-Server/src/test/java/com/mentionall/cpr2u/call/repository/FakeDispatchRepository.java b/CPR2U-Server/src/test/java/com/mentionall/cpr2u/call/repository/FakeDispatchRepository.java deleted file mode 100644 index 906be13..0000000 --- a/CPR2U-Server/src/test/java/com/mentionall/cpr2u/call/repository/FakeDispatchRepository.java +++ /dev/null @@ -1,178 +0,0 @@ -package com.mentionall.cpr2u.call.repository; - -import com.mentionall.cpr2u.call.domain.Dispatch; -import org.springframework.data.domain.Example; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Sort; -import org.springframework.data.repository.query.FluentQuery; - -import java.util.*; -import java.util.function.Function; - -public class FakeDispatchRepository implements DispatchRepository { - Map map = new HashMap(); - - @Override - public List findAll() { - return null; - } - - @Override - public List findAll(Sort sort) { - return null; - } - - @Override - public Page findAll(Pageable pageable) { - return null; - } - - @Override - public List findAllById(Iterable longs) { - return null; - } - - @Override - public long count() { - return 0; - } - - @Override - public void deleteById(Long aLong) { - - } - - @Override - public void delete(Dispatch entity) { - - } - - @Override - public void deleteAllById(Iterable longs) { - - } - - @Override - public void deleteAll(Iterable entities) { - - } - - @Override - public void deleteAll() { - - } - - @Override - public S save(S entity) { - map.put(entity.getId(), entity); - return entity; - } - - @Override - public List saveAll(Iterable entities) { - return null; - } - - @Override - public Optional findById(Long aLong) { - return Optional.of((Dispatch) map.get(aLong)); - } - - @Override - public boolean existsById(Long aLong) { - return false; - } - - @Override - public void flush() { - - } - - @Override - public S saveAndFlush(S entity) { - return null; - } - - @Override - public List saveAllAndFlush(Iterable entities) { - return null; - } - - @Override - public void deleteAllInBatch(Iterable entities) { - - } - - @Override - public void deleteAllByIdInBatch(Iterable longs) { - - } - - @Override - public void deleteAllInBatch() { - - } - - @Override - public Dispatch getOne(Long aLong) { - return null; - } - - @Override - public Dispatch getById(Long aLong) { - return null; - } - - @Override - public Dispatch getReferenceById(Long aLong) { - return null; - } - - @Override - public Optional findOne(Example example) { - return Optional.empty(); - } - - @Override - public List findAll(Example example) { - return null; - } - - @Override - public List findAll(Example example, Sort sort) { - return null; - } - - @Override - public Page findAll(Example example, Pageable pageable) { - return null; - } - - @Override - public long count(Example example) { - return 0; - } - - @Override - public boolean exists(Example example) { - return false; - } - - @Override - public R findBy(Example example, Function, R> queryFunction) { - return null; - } - - @Override - public List findAllByCprCallId(Long cprCallId) { - List dispatchList = new ArrayList<>(); - for(Object object : map.values()){ - Dispatch dispatch = (Dispatch) object; - if(dispatch.getCprCall().getId() == cprCallId){ - dispatchList.add(dispatch); - } - } - return dispatchList; - } -} diff --git a/CPR2U-Server/src/test/java/com/mentionall/cpr2u/call/repository/FakeReportRepository.java b/CPR2U-Server/src/test/java/com/mentionall/cpr2u/call/repository/FakeReportRepository.java deleted file mode 100644 index 32c416b..0000000 --- a/CPR2U-Server/src/test/java/com/mentionall/cpr2u/call/repository/FakeReportRepository.java +++ /dev/null @@ -1,178 +0,0 @@ -package com.mentionall.cpr2u.call.repository; - -import com.mentionall.cpr2u.call.domain.Dispatch; -import com.mentionall.cpr2u.call.domain.Report; -import com.mentionall.cpr2u.user.domain.User; -import org.springframework.data.domain.Example; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Sort; -import org.springframework.data.repository.query.FluentQuery; - -import java.util.*; -import java.util.function.Function; - -public class FakeReportRepository implements ReportRepository { - Map map = new HashMap(); - - @Override - public List findAll() { - return null; - } - - @Override - public List findAll(Sort sort) { - return null; - } - - @Override - public Page findAll(Pageable pageable) { - return null; - } - - @Override - public List findAllById(Iterable longs) { - return null; - } - - @Override - public long count() { - return 0; - } - - @Override - public void deleteById(Long aLong) { - - } - - @Override - public void delete(Report entity) { - - } - - @Override - public void deleteAllById(Iterable longs) { - - } - - @Override - public void deleteAll(Iterable entities) { - - } - - @Override - public void deleteAll() { - - } - - @Override - public S save(S entity) { - map.put(1L, entity); - return entity; - } - - @Override - public List saveAll(Iterable entities) { - return null; - } - - @Override - public Optional findById(Long aLong) { - return Optional.of((Report) map.get(aLong)); - } - - @Override - public boolean existsById(Long aLong) { - return false; - } - - @Override - public void flush() { - - } - - @Override - public S saveAndFlush(S entity) { - return null; - } - - @Override - public List saveAllAndFlush(Iterable entities) { - return null; - } - - @Override - public void deleteAllInBatch(Iterable entities) { - - } - - @Override - public void deleteAllByIdInBatch(Iterable longs) { - - } - - @Override - public void deleteAllInBatch() { - - } - - @Override - public Report getOne(Long aLong) { - return null; - } - - @Override - public Report getById(Long aLong) { - return null; - } - - @Override - public Report getReferenceById(Long aLong) { - return null; - } - - @Override - public Optional findOne(Example example) { - return Optional.empty(); - } - - @Override - public List findAll(Example example) { - return null; - } - - @Override - public List findAll(Example example, Sort sort) { - return null; - } - - @Override - public Page findAll(Example example, Pageable pageable) { - return null; - } - - @Override - public long count(Example example) { - return 0; - } - - @Override - public boolean exists(Example example) { - return false; - } - - @Override - public R findBy(Example example, Function, R> queryFunction) { - return null; - } - - @Override - public List findAllByReporter(User user) { - List list = new ArrayList(); - for (Long key : map.keySet()) { - Report report = (Report) map.get(key); - if (report.getReporter().getId() == user.getId()) list.add(report); - } - return list; - } -} diff --git a/CPR2U-Server/src/test/java/com/mentionall/cpr2u/call/service/CprCallServiceTest.java b/CPR2U-Server/src/test/java/com/mentionall/cpr2u/call/service/CprCallServiceTest.java index 82a8841..80fc7de 100644 --- a/CPR2U-Server/src/test/java/com/mentionall/cpr2u/call/service/CprCallServiceTest.java +++ b/CPR2U-Server/src/test/java/com/mentionall/cpr2u/call/service/CprCallServiceTest.java @@ -1,165 +1,158 @@ package com.mentionall.cpr2u.call.service; -import com.fasterxml.jackson.databind.ObjectMapper; import com.mentionall.cpr2u.call.domain.CprCall; import com.mentionall.cpr2u.call.domain.CprCallStatus; import com.mentionall.cpr2u.call.domain.Dispatch; import com.mentionall.cpr2u.call.domain.DispatchStatus; -import com.mentionall.cpr2u.call.dto.CprCallOccurDto; -import com.mentionall.cpr2u.call.dto.DispatchRequestDto; -import com.mentionall.cpr2u.call.dto.DispatchResponseDto; -import com.mentionall.cpr2u.call.repository.*; -import com.mentionall.cpr2u.config.security.JwtTokenProvider; -import com.mentionall.cpr2u.education.repository.EducationProgressRepository; +import com.mentionall.cpr2u.call.dto.cpr_call.CprCallRequestDto; +import com.mentionall.cpr2u.call.dto.dispatch.DispatchRequestDto; +import com.mentionall.cpr2u.call.repository.CprCallRepository; +import com.mentionall.cpr2u.call.repository.DispatchRepository; +import com.mentionall.cpr2u.call.util.FakeFirebaseCloudMessageUtil; import com.mentionall.cpr2u.user.domain.Address; import com.mentionall.cpr2u.user.domain.User; -import com.mentionall.cpr2u.user.dto.UserSignUpDto; -import com.mentionall.cpr2u.user.repository.*; -import com.mentionall.cpr2u.user.service.UserService; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; +import com.mentionall.cpr2u.user.dto.user.SignUpRequestDto; +import com.mentionall.cpr2u.user.repository.UserRepository; +import com.mentionall.cpr2u.user.repository.address.AddressRepository; +import com.mentionall.cpr2u.user.service.AddressService; +import com.mentionall.cpr2u.user.service.AuthService; +import com.mentionall.cpr2u.util.fcm.FirebaseCloudMessageUtil; +import org.junit.jupiter.api.*; import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; import javax.transaction.Transactional; import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.UUID; import static org.assertj.core.api.Assertions.assertThat; @SpringBootTest -@ExtendWith(MockitoExtension.class) +@DisplayName("호좜 κ΄€λ ¨ ν…ŒμŠ€νŠΈ") +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) class CprCallServiceTest { - - + @Autowired private CprCallService cprCallService; - private DispatchService dispatchService; - - private UserService userService; + @Autowired + private FakeFirebaseCloudMessageUtil firebaseCloudMessageUtil; + @Autowired private CprCallRepository cprCallRepository; - + @Autowired + private AuthService authService; + @Autowired private UserRepository userRepository; - + @Autowired + private DispatchService dispatchService; + @Autowired private DispatchRepository dispatchRepository; - + @Autowired + private AddressService addressService; + @Autowired private AddressRepository addressRepository; - private DeviceTokenRepository deviceTokenRepository; - - private EducationProgressRepository progressRepository; - - private JwtTokenProvider jwtTokenProvider; - - private RefreshTokenRepository refreshTokenRepository; - - @BeforeEach - public void beforeEach() { - this.dispatchRepository = new FakeDispatchRepository(); - this.userRepository = new FakeUserRepository(); - this.addressRepository = new FakeAddressRepository(); - this.cprCallRepository = new FakeCprCallRepository(); - this.deviceTokenRepository = new FakeDeviceTokenRepository(); - this.cprCallService = new CprCallService(cprCallRepository, dispatchRepository, addressRepository, deviceTokenRepository, new FirebaseCloudMessageService(null)); - this.dispatchService = new DispatchService(dispatchRepository, cprCallRepository, null); - } + private static final double latitude = 37.56559872345163; + private static final double longitude = 126.9771473198163; + private static final String userPhoneNumber = "010-0000-0000"; + private static final String angelPhoneNumber = "010-1111-1111"; + private static final String testFullAddress1 = "μ„œμšΈ μ’…λ‘œκ΅¬ μ’…λ‘œ 104"; + private static final String testFullAddress2 = "μ„œμšΈ 쀑ꡬ μ„Έμ’…λŒ€λ‘œ μ§€ν•˜ 2"; + private static final String testFullAddress3 = "μ„Έμ’…νŠΉλ³„μžμΉ˜μ‹œ ν•œλˆ„λ¦¬λŒ€λ‘œ 2130 (우)30151"; + private static final String testFullAddress4 = "경상남도 μ°½μ›μ‹œ 진해ꡬ ν‰μ•ˆλ™ 10"; @BeforeEach - public void insertData() { - User dispatcher = userRepository.save(new User(new UserSignUpDto("μΆœλ™μž", "phoneNumber" + 1, UUID.randomUUID().toString()))); - User caller = userRepository.save(new User(new UserSignUpDto("호좜자", "phoneNumber" + 2, UUID.randomUUID().toString()))); - Address address1 = addressRepository.save(new Address(1L, "μ„œμšΈμ‹œ", "μš©μ‚°κ΅¬", new ArrayList<>())); - Address address2 = addressRepository.save(new Address(2L, "μ„œμšΈμ‹œ", "λ™μž‘κ΅¬", new ArrayList<>())); - Address address3 = addressRepository.save(new Address(3L, "μ„œμšΈμ‹œ", "μ’…λ‘œκ΅¬", new ArrayList<>())); - Address address4 = addressRepository.save(new Address(4L, "μ„Έμ’…νŠΉλ³„μžμΉ˜μ‹œ", "", new ArrayList<>())); - Address address5 = addressRepository.save(new Address(5L, "경남", "μ°½μ›μ‹œ", new ArrayList<>())); - cprCallRepository.save(new CprCall(1L, caller, address1, "μ„œμšΈμ‹œ μš©μ‚°κ΅¬ μ–΄μ©Œκ΅¬", - LocalDateTime.now(), 37.542547, 126.963796, CprCallStatus.IN_PROGRESS, - new ArrayList<>(), new ArrayList<>())); + private void beforeEach() { + addressService.loadAddressList(); } - //TODO: ν…ŒμŠ€νŠΈ μ‹€νŒ¨ - address DB 데이터가 μ—†λŠ” 경우, addressλ₯Ό 찾지 λͺ»ν•˜κ³  μ‹€νŒ¨ @Test @Transactional - @DisplayName("μ—”μ € μœ μ €λ“€μ˜ 근처 호좜 쑰회") - public void getNowCallStatusNearUser() { + public void 호좜_주변에_엔저이_μžˆλŠ”_경우() { //given + createUsers(); + User caller = userRepository.findByPhoneNumber(userPhoneNumber).get(); + User cprAngel = userRepository.findByPhoneNumber(angelPhoneNumber).get(); - Address address1 = addressRepository.findById(1L).get(); - Address address2 = addressRepository.findById(2L).get(); + cprCallService.makeCall(new CprCallRequestDto(testFullAddress1, latitude, longitude), caller); - //CPR 엔저이고 ν™˜μžλ„ 있음 - User cprAngelUser = userRepository.findByPhoneNumber("phoneNumber" + 1).get(); - cprAngelUser.setAddress(address1); - cprAngelUser.acquireCertification(); - userRepository.save(cprAngelUser); + //when + var callListForAngel = cprCallService.getCallNearUser(cprAngel); - //CPR μ—”μ €μ΄μ§€λ§Œ ν™˜μžκ°€ μ—†λŠ” 동넀에 있음 - User cprAngelUserButNoPatient = userRepository.findByPhoneNumber("phoneNumber" + 2).get(); - cprAngelUserButNoPatient.setAddress(address2); - cprAngelUserButNoPatient.acquireCertification(); - userRepository.save(cprAngelUserButNoPatient); + //then + assertThat(callListForAngel.getCprCallResponseDtoList().size()).isEqualTo(1); + } + + @Test + @Transactional + public void 호좜_주변에_일반인이_μžˆλŠ”_경우() { + //given + createUsers(); + User caller = userRepository.findByPhoneNumber(angelPhoneNumber).get(); + User notAngel = userRepository.findByPhoneNumber(userPhoneNumber).get(); - //cpr 엔저이 μ•„λ‹Œλ° ν™˜μžκ°€ μžˆλŠ” 동넀에 있음 - User notAngel = userRepository.findByPhoneNumber("phoneNumber" + 3).get(); - notAngel.setAddress(address1); - userRepository.save(notAngel); + cprCallService.makeCall(new CprCallRequestDto(testFullAddress1, latitude, longitude), caller); //when - var callListForAngel = cprCallService.getCallNearUser(cprAngelUser); - var callListForAngelButNoPatient = cprCallService.getCallNearUser(cprAngelUserButNoPatient); var callListForNotAngel = cprCallService.getCallNearUser(notAngel); //then - assertThat(callListForAngel.getCprCallDtoList().size()).isEqualTo(1); - assertThat(callListForAngelButNoPatient.getCprCallDtoList().size()).isEqualTo(0); - assertThat(callListForNotAngel.getCprCallDtoList().size()).isEqualTo(0); - + assertThat(callListForNotAngel.getCprCallResponseDtoList().size()).isEqualTo(0); } - //TODO: ν…ŒμŠ€νŠΈ μ‹€νŒ¨ - address DB 데이터가 μ—†λŠ” 경우, addressλ₯Ό 찾지 λͺ»ν•˜κ³  μ‹€νŒ¨ @Test @Transactional - @DisplayName("호좜 생성") - public void makeCall() { + public void 호좜_주변에_만료된_엔저이_μžˆλŠ”_경우() { //given - User user = userRepository.findByPhoneNumber("phoneNumber" + 1).get(); + createUsers(); + User caller = userRepository.findByPhoneNumber(angelPhoneNumber).get(); + User expiredAngel = userRepository.findByPhoneNumber(userPhoneNumber).get(); + expiredAngel.expireCertificate(); + + cprCallService.makeCall(new CprCallRequestDto(testFullAddress1, latitude, longitude), caller); //when - Long callId1 = cprCallService.makeCall(new CprCallOccurDto("μ„œμšΈ μš©μ‚°κ΅¬ 청파둜47κΈΈ 100", 37.56559872345163, 126.9779734762639), user).getCallId(); - Long callId2 = cprCallService.makeCall(new CprCallOccurDto("μ„œμšΈ μ’…λ‘œκ΅¬ μ’…λ‘œ 104", 37.56559872345163, 126.9779734762639), user).getCallId(); - Long callId3 = cprCallService.makeCall(new CprCallOccurDto("μ„Έμ’…νŠΉλ³„μžμΉ˜μ‹œ ν•œλˆ„λ¦¬λŒ€λ‘œ 2130 (우)30151", 37.56559872345163, 126.9779734762639), user).getCallId(); - Long callId4 = cprCallService.makeCall(new CprCallOccurDto("경남 μ°½μ›μ‹œ 진해ꡬ ν‰μ•ˆλ™ 10", 37.56559872345163, 126.9779734762639), user).getCallId(); + var callListForExpiredAngel = cprCallService.getCallNearUser(expiredAngel); //then - CprCall cprCall1 = cprCallRepository.findById(callId1).get(); - CprCall cprCall2 = cprCallRepository.findById(callId2).get(); - CprCall cprCall3 = cprCallRepository.findById(callId3).get(); - CprCall cprCall4 = cprCallRepository.findById(callId4).get(); + assertThat(callListForExpiredAngel.getCprCallResponseDtoList().size()).isEqualTo(0); + } + + @Test + @Transactional + public void 호좜_μ’…λ£Œ() { + //given + createUsers(); + User caller = userRepository.findByPhoneNumber(userPhoneNumber).get(); + User cprAngel = userRepository.findByPhoneNumber(angelPhoneNumber).get(); - assertThat(cprCall1.getAddress().getId()).isEqualTo(1L); - assertThat(cprCall2.getAddress().getId()).isEqualTo(3L); - assertThat(cprCall3.getAddress().getId()).isEqualTo(4L); - assertThat(cprCall4.getAddress().getId()).isEqualTo(5L); + Long callId = cprCallService.makeCall(new CprCallRequestDto(testFullAddress1, latitude, longitude), caller).getCallId(); + cprCallService.endCall(callId); - assertThat(cprCall1.getStatus()).isEqualTo(CprCallStatus.IN_PROGRESS); + //when + var callListForAngel = cprCallService.getCallNearUser(cprAngel); + //then + assertThat(callListForAngel.getCprCallResponseDtoList().size()).isEqualTo(0); } @Test @Transactional - @DisplayName("호좜 μ’…λ£Œ") - void endCall() { + public void 호좜_μ’…λ£Œ_μΆœλ™ν•œ_엔저이_μžˆλŠ”_경우() { //given - User caller = userRepository.findByPhoneNumber("phoneNumber" + 1).get(); - User dispatcher = userRepository.findByPhoneNumber("phoneNumber" + 2).get(); + createUsers(); + User caller = userRepository.findByPhoneNumber(userPhoneNumber).get(); + User dispatcher = userRepository.findByPhoneNumber(angelPhoneNumber).get(); + + Long callId = cprCallService + .makeCall(new CprCallRequestDto(testFullAddress1, latitude, longitude), caller) + .getCallId(); - Long callId = cprCallService.makeCall(new CprCallOccurDto("μ„œμšΈ μš©μ‚°κ΅¬ 청파둜47κΈΈ 100", 37.56559872345163, 126.9779734762639), caller).getCallId(); - DispatchResponseDto dispatchInfo = dispatchService.dispatch(dispatcher, new DispatchRequestDto(callId)); + var dispatchInfo = dispatchService.dispatch(dispatcher, new DispatchRequestDto(callId)); //when cprCallService.endCall(callId); @@ -174,40 +167,81 @@ void endCall() { @Test @Transactional - @DisplayName("μ‹€μ‹œκ°„ μΆœλ™ν•œ μ—”μ € 수 μ•ˆλ‚΄") - void getNumberOfAngelsDispatched() { + public void 지역별_호좜_생성() { //given - User caller = userRepository.findByPhoneNumber("phoneNumber" + 1).get(); - User dispatcher = userRepository.findByPhoneNumber("phoneNumber" + 2).get(); + createUsers(); + User user = userRepository.findByPhoneNumber(userPhoneNumber).get(); + Address testAddress1 = addressRepository.findByFullAddress(testFullAddress1).get(); + Address testAddress2 = addressRepository.findByFullAddress(testFullAddress2).get(); + Address testAddress3 = addressRepository.findByFullAddress(testFullAddress3).get(); + Address testAddress4 = addressRepository.findByFullAddress(testFullAddress4).get(); + + //when + Long callId1 = cprCallService.makeCall(new CprCallRequestDto(testFullAddress1, latitude, longitude), user).getCallId(); + Long callId2 = cprCallService.makeCall(new CprCallRequestDto(testFullAddress2, latitude, longitude), user).getCallId(); + Long callId3 = cprCallService.makeCall(new CprCallRequestDto(testFullAddress3, latitude, longitude), user).getCallId(); + Long callId4 = cprCallService.makeCall(new CprCallRequestDto(testFullAddress4, latitude, longitude), user).getCallId(); - Long callId = cprCallService.makeCall(new CprCallOccurDto("μ„œμšΈμ‹œ λ™μž‘κ΅¬", 37.56559872345163, 126.9779734762639), caller).getCallId(); + //then + CprCall cprCall1 = cprCallRepository.findById(callId1).get(); + CprCall cprCall2 = cprCallRepository.findById(callId2).get(); + CprCall cprCall3 = cprCallRepository.findById(callId3).get(); + CprCall cprCall4 = cprCallRepository.findById(callId4).get(); + + assertThat(cprCall1.getAddress().getId()).isEqualTo(testAddress1.getId()); + assertThat(cprCall2.getAddress().getId()).isEqualTo(testAddress2.getId()); + assertThat(cprCall3.getAddress().getId()).isEqualTo(testAddress3.getId()); + assertThat(cprCall4.getAddress().getId()).isEqualTo(testAddress4.getId()); - //when no one dispatched - var noOneDispatched = cprCallService.getNumberOfAngelsDispatched(callId); + assertThat(cprCall1.getStatus()).isEqualTo(CprCallStatus.IN_PROGRESS); + + } + + @Test + @Transactional + public void μ‹€μ‹œκ°„_μΆœλ™_μ•ˆλ‚΄_μΆœλ™ν•œ_엔저이_μ—†λŠ”_경우() { + //given + createUsers(); + User caller = userRepository.findByPhoneNumber(userPhoneNumber).get(); + Long callId = cprCallService.makeCall(new CprCallRequestDto(testFullAddress1, latitude, longitude), caller) + .getCallId(); + + //when + var callGuide = cprCallService.getNumberOfAngelsDispatched(callId); //then - assertThat(noOneDispatched.getNumberOfAngels()).isEqualTo(0); + assertThat(callGuide.getNumberOfAngels()).isEqualTo(0); + } - //when 1 angel dispatched + @Test + @Transactional + public void μ‹€μ‹œκ°„_μΆœλ™_μ•ˆλ‚΄_μΆœλ™ν•œ_엔저이_μžˆλŠ”_경우() { + //given + createUsers(); + User caller = userRepository.findByPhoneNumber(userPhoneNumber).get(); + User dispatcher = userRepository.findByPhoneNumber(angelPhoneNumber).get(); + + Long callId = cprCallService.makeCall(new CprCallRequestDto(testFullAddress1, latitude, longitude), caller).getCallId(); dispatchService.dispatch(dispatcher, new DispatchRequestDto(callId)); - var oneAngelDispatched = cprCallService.getNumberOfAngelsDispatched(callId); + + //when + var callGuide = cprCallService.getNumberOfAngelsDispatched(callId); //then - assertThat(oneAngelDispatched.getNumberOfAngels()).isEqualTo(1); + assertThat(callGuide.getNumberOfAngels()).isEqualTo(1); } - public void registerUserWithNumber(int number) { - UserSignUpDto userSignUpDto = new UserSignUpDto("nickname" + number, "phoneNumber" + number, "deviceToken"); - userService.signup(userSignUpDto); - } + private void createUsers() { - public void registerUserWithNumberAndAddress(int number, Address address) { - UserSignUpDto userSignUpDto = new UserSignUpDto("nickname" + number, "phoneNumber" + number, "deviceToken"); - userService.signup(userSignUpDto); + String userNickname = "μ˜ˆμ§„"; + String angelNickname = "ν˜„μ• "; + String deviceToken = "device-code"; + var address = addressRepository.findByFullAddress(testFullAddress1).get(); + authService.signup(new SignUpRequestDto(userNickname, userPhoneNumber, address.getId(), deviceToken)); + authService.signup(new SignUpRequestDto(angelNickname, angelPhoneNumber, address.getId(), deviceToken)); - User user = userRepository.findByPhoneNumber("phoneNumber" + number).get(); - user.setAddress(address); - user.acquireCertification(); - userRepository.save(user); + User angel = userRepository.findByPhoneNumber(angelPhoneNumber).get(); + angel.acquireCertification(LocalDateTime.now()); + userRepository.save(angel); } } \ No newline at end of file diff --git a/CPR2U-Server/src/test/java/com/mentionall/cpr2u/call/service/DispatchServiceTest.java b/CPR2U-Server/src/test/java/com/mentionall/cpr2u/call/service/DispatchServiceTest.java index be5ea90..47548f9 100644 --- a/CPR2U-Server/src/test/java/com/mentionall/cpr2u/call/service/DispatchServiceTest.java +++ b/CPR2U-Server/src/test/java/com/mentionall/cpr2u/call/service/DispatchServiceTest.java @@ -1,109 +1,148 @@ package com.mentionall.cpr2u.call.service; -import com.mentionall.cpr2u.call.domain.*; -import com.mentionall.cpr2u.call.dto.DispatchRequestDto; +import com.mentionall.cpr2u.call.domain.CprCall; +import com.mentionall.cpr2u.call.domain.Dispatch; +import com.mentionall.cpr2u.call.domain.DispatchStatus; +import com.mentionall.cpr2u.call.domain.Report; import com.mentionall.cpr2u.call.dto.ReportRequestDto; -import com.mentionall.cpr2u.call.repository.*; -import com.mentionall.cpr2u.user.domain.Address; +import com.mentionall.cpr2u.call.dto.cpr_call.CprCallRequestDto; +import com.mentionall.cpr2u.call.dto.dispatch.DispatchRequestDto; +import com.mentionall.cpr2u.call.repository.CprCallRepository; +import com.mentionall.cpr2u.call.repository.DispatchRepository; +import com.mentionall.cpr2u.call.repository.ReportRepository; import com.mentionall.cpr2u.user.domain.User; -import com.mentionall.cpr2u.user.dto.UserSignUpDto; -import com.mentionall.cpr2u.user.repository.AddressRepository; -import com.mentionall.cpr2u.user.repository.FakeAddressRepository; -import com.mentionall.cpr2u.user.repository.FakeUserRepository; +import com.mentionall.cpr2u.user.dto.address.AddressResponseDto; +import com.mentionall.cpr2u.user.dto.user.SignUpRequestDto; import com.mentionall.cpr2u.user.repository.UserRepository; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; +import com.mentionall.cpr2u.user.service.AddressService; +import com.mentionall.cpr2u.user.service.AuthService; +import org.junit.jupiter.api.*; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -import java.time.LocalDateTime; -import java.util.ArrayList; +import javax.transaction.Transactional; import java.util.List; -import java.util.UUID; import static org.assertj.core.api.Assertions.assertThat; @SpringBootTest +@DisplayName("μΆœλ™ κ΄€λ ¨ ν…ŒμŠ€νŠΈ") +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) public class DispatchServiceTest { + @Autowired private DispatchService dispatchService; + @Autowired private DispatchRepository dispatchRepository; - private CprCallRepository callRepository; + @Autowired + private AuthService authService; + @Autowired private UserRepository userRepository; + @Autowired + private CprCallService callService; + @Autowired + private CprCallRepository callRepository; + @Autowired + private AddressService addressService; + @Autowired private ReportRepository reportRepository; - private AddressRepository addressRepository; - @BeforeEach - public void beforeEach() { - this.dispatchRepository = new FakeDispatchRepository(); - this.callRepository = new FakeCprCallRepository(); - this.userRepository = new FakeUserRepository(); - this.reportRepository = new FakeReportRepository(); - this.addressRepository = new FakeAddressRepository(); - this.dispatchService = new DispatchService(dispatchRepository, callRepository, reportRepository); - } + private static final String fullAddress = "μ„œμšΈνŠΉλ³„μ‹œ μš©μ‚°κ΅¬ 청파둜47κΈΈ 100"; + private static final double latitude = 37.545183430559604; + private static final double longitude = 126.9648022541866; + private static final String callerPhoneNumber = "010-0000-0000"; + private static final String dispatcherPhoneNumber = "010-0000-0001"; @BeforeEach - public void insertData() { - User dispatcher = userRepository.save(new User(new UserSignUpDto("μΆœλ™μž", "+821000000000", UUID.randomUUID().toString()))); - User caller = userRepository.save(new User(new UserSignUpDto("호좜자", "+821011111111", UUID.randomUUID().toString()))); - Address address = addressRepository.save(new Address(1L, "μ„œμšΈμ‹œ", "μš©μ‚°κ΅¬", new ArrayList<>())); - callRepository.save(new CprCall(1L, caller, address, "μ„œμšΈμ‹œ μš©μ‚°κ΅¬ μ–΄μ©Œκ΅¬", - LocalDateTime.now(), 37.542547, 126.963796, CprCallStatus.IN_PROGRESS, - new ArrayList<>(), new ArrayList<>())); + public void beforeEach() { + addressService.loadAddressList(); } @Test - @DisplayName("CPR μΆœλ™") - public void dispatch() { + @Transactional + public void CPR_μΆœλ™_ν˜ΈμΆœμƒν™©_정보_쑰회() { //given - User user = userRepository.findById("1").get(); - CprCall cprCall = callRepository.findById(1L).get(); + createCallerAndDispatcher(); + User caller = userRepository.findByPhoneNumber(callerPhoneNumber).get(); + User dispatcher = userRepository.findByPhoneNumber(dispatcherPhoneNumber).get(); + + long callId = callService.makeCall(new CprCallRequestDto(fullAddress, latitude, longitude), caller).getCallId(); //when - var response = dispatchService.dispatch(user, new DispatchRequestDto(cprCall.getId())); + var dispatchInfo = dispatchService.dispatch(dispatcher, new DispatchRequestDto(callId)); //then - assertThat(response.getCalledAt()).isEqualTo(cprCall.getCalledAt()); - assertThat(response.getLatitude()).isEqualTo(37.542547); - assertThat(response.getLongitude()).isEqualTo(126.963796); - assertThat(response.getFullAddress()).isEqualTo("μ„œμšΈμ‹œ μš©μ‚°κ΅¬ μ–΄μ©Œκ΅¬"); + CprCall cprCall = callRepository.findById(callId).get(); + assertThat(dispatchInfo.getCalledAt()).isEqualTo(cprCall.getCalledAt()); + assertThat(dispatchInfo.getLatitude()).isEqualTo(latitude); + assertThat(dispatchInfo.getLongitude()).isEqualTo(longitude); + assertThat(dispatchInfo.getFullAddress()).isEqualTo(fullAddress); + } + + @Test + @Transactional + public void CPR_μΆœλ™_μ‹œ_μΆœλ™μƒνƒœ_진행쀑() { + //given + createCallerAndDispatcher(); + User caller = userRepository.findByPhoneNumber(callerPhoneNumber).get(); + User dispatcher = userRepository.findByPhoneNumber(dispatcherPhoneNumber).get(); - Dispatch dispatch = dispatchRepository.findById(response.getDispatchId()).get(); + long callId = callService.makeCall(new CprCallRequestDto(fullAddress, latitude, longitude), caller).getCallId(); + + //when + var callInfo = dispatchService.dispatch(dispatcher, new DispatchRequestDto(callId)); + + //then + Dispatch dispatch = dispatchRepository.findById(callInfo.getDispatchId()).get(); assertThat(dispatch.getStatus()).isEqualTo(DispatchStatus.IN_PROGRESS); } @Test - @DisplayName("CPR μΆœλ™ 도착") - public void arrive() { + @Transactional + public void CPR_μΆœλ™_도착_μ‹œ_μΆœλ™μƒνƒœ_도착() { //given - User user = userRepository.findById("1").get(); - CprCall cprCall = callRepository.findById(1L).get(); + createCallerAndDispatcher(); + User caller = userRepository.findByPhoneNumber(callerPhoneNumber).get(); + User dispatcher = userRepository.findByPhoneNumber(dispatcherPhoneNumber).get(); + + long callId = callService.makeCall(new CprCallRequestDto(fullAddress, latitude, longitude), caller).getCallId(); + var dispatchInfo = dispatchService.dispatch(dispatcher, new DispatchRequestDto(callId)); //when - var response = dispatchService.dispatch(user, new DispatchRequestDto(cprCall.getId())); - dispatchService.arrive(response.getDispatchId()); + dispatchService.arrive(dispatchInfo.getDispatchId()); //then - var dispatchArrived = dispatchRepository.findById(response.getDispatchId()).get(); - assertThat(dispatchArrived.getStatus()).isEqualTo(DispatchStatus.ARRIVED); + var dispatch = dispatchRepository.findById(dispatchInfo.getDispatchId()).get(); + assertThat(dispatch.getStatus()).isEqualTo(DispatchStatus.ARRIVED); } @Test - @DisplayName("μΆœλ™ μ‹ κ³ ") - public void report() { + @Transactional + public void CPR_ν—ˆμœ„_호좜_μ‹ κ³ () { //given - User user = userRepository.findById("1").get(); - CprCall cprCall = callRepository.findById(1L).get(); + createCallerAndDispatcher(); + User caller = userRepository.findByPhoneNumber(callerPhoneNumber).get(); + User dispatcher = userRepository.findByPhoneNumber(dispatcherPhoneNumber).get(); + + long callId = callService.makeCall(new CprCallRequestDto(fullAddress, latitude, longitude), caller).getCallId(); + var dispatchInfo = dispatchService.dispatch(dispatcher, new DispatchRequestDto(callId)); //when - var response = dispatchService.dispatch(user, new DispatchRequestDto(cprCall.getId())); - dispatchService.report(new ReportRequestDto(response.getDispatchId(), "μ‹ κ³  λ‚΄μš©")); + dispatchService.report(new ReportRequestDto(dispatchInfo.getDispatchId(), "μ‹ κ³  λ‚΄μš©")); //then - List reportList = reportRepository.findAllByReporter(user); + List reportList = reportRepository.findAllByReporter(dispatcher); assertThat(reportList.size()).isEqualTo(1); - assertThat(reportList.get(0).getCprCall().getId()).isEqualTo(1L); + assertThat(reportList.get(0).getCprCall().getId()).isEqualTo(callId); assertThat(reportList.get(0).getContent()).isEqualTo("μ‹ κ³  λ‚΄μš©"); } + + private void createCallerAndDispatcher() { + List addressList = addressService.readAll(); + var address = addressList.get(0); + var addressDetail = address.getGugunList().get(0); + + authService.signup(new SignUpRequestDto("호좜자", "010-0000-0000", addressDetail.getId(), "device_token")); + authService.signup(new SignUpRequestDto("μΆœλ™μž", "010-0000-0001", addressDetail.getId(), "device_token")); + } } diff --git a/CPR2U-Server/src/test/java/com/mentionall/cpr2u/call/util/FakeFirebaseCloudMessageUtil.java b/CPR2U-Server/src/test/java/com/mentionall/cpr2u/call/util/FakeFirebaseCloudMessageUtil.java new file mode 100644 index 0000000..16b4820 --- /dev/null +++ b/CPR2U-Server/src/test/java/com/mentionall/cpr2u/call/util/FakeFirebaseCloudMessageUtil.java @@ -0,0 +1,14 @@ +package com.mentionall.cpr2u.call.util; + +import com.mentionall.cpr2u.util.fcm.FirebaseCloudMessageUtil; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Map; + +public class FakeFirebaseCloudMessageUtil extends FirebaseCloudMessageUtil { + + @Override + public void sendFcmMessage(List deviceTokenToSendList, String title, String body, Map data) { + } +} diff --git a/CPR2U-Server/src/test/java/com/mentionall/cpr2u/education/repository/FakeEducationProgressRepository.java b/CPR2U-Server/src/test/java/com/mentionall/cpr2u/education/repository/FakeEducationProgressRepository.java deleted file mode 100644 index 9c35ec7..0000000 --- a/CPR2U-Server/src/test/java/com/mentionall/cpr2u/education/repository/FakeEducationProgressRepository.java +++ /dev/null @@ -1,181 +0,0 @@ -package com.mentionall.cpr2u.education.repository; - -import com.mentionall.cpr2u.education.domain.EducationProgress; -import com.mentionall.cpr2u.user.domain.User; -import org.springframework.data.domain.Example; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Sort; -import org.springframework.data.repository.query.FluentQuery; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.function.Function; - -public class FakeEducationProgressRepository implements EducationProgressRepository{ - - Map map = new HashMap(); - - @Override - public Optional findByUser(User user) { - for (var key : map.keySet()) { - EducationProgress progress = (EducationProgress) map.get(key); - if (progress.getUser().getId() == user.getId()) - return Optional.of(progress); - } - return Optional.empty(); - } - - @Override - public List findAll() { - return null; - } - - @Override - public List findAll(Sort sort) { - return null; - } - - @Override - public Page findAll(Pageable pageable) { - return null; - } - - @Override - public List findAllById(Iterable longs) { - return null; - } - - @Override - public long count() { - return 0; - } - - @Override - public void deleteById(Long aLong) { - - } - - @Override - public void delete(EducationProgress entity) { - - } - - @Override - public void deleteAllById(Iterable longs) { - - } - - @Override - public void deleteAll(Iterable entities) { - - } - - @Override - public void deleteAll() { - - } - - @Override - public S save(S entity) { - map.put(entity.getId(), entity); - return entity; - } - - @Override - public List saveAll(Iterable entities) { - return null; - } - - @Override - public Optional findById(Long aLong) { - return Optional.of((EducationProgress) map.get(aLong)); - } - - @Override - public boolean existsById(Long aLong) { - return false; - } - - @Override - public void flush() { - - } - - @Override - public S saveAndFlush(S entity) { - return null; - } - - @Override - public List saveAllAndFlush(Iterable entities) { - return null; - } - - @Override - public void deleteAllInBatch(Iterable entities) { - - } - - @Override - public void deleteAllByIdInBatch(Iterable longs) { - - } - - @Override - public void deleteAllInBatch() { - - } - - @Override - public EducationProgress getOne(Long aLong) { - return null; - } - - @Override - public EducationProgress getById(Long aLong) { - return null; - } - - @Override - public EducationProgress getReferenceById(Long aLong) { - return null; - } - - @Override - public Optional findOne(Example example) { - return Optional.empty(); - } - - @Override - public List findAll(Example example) { - return null; - } - - @Override - public List findAll(Example example, Sort sort) { - return null; - } - - @Override - public Page findAll(Example example, Pageable pageable) { - return null; - } - - @Override - public long count(Example example) { - return 0; - } - - @Override - public boolean exists(Example example) { - return false; - } - - @Override - public R findBy(Example example, Function, R> queryFunction) { - return null; - } -} diff --git a/CPR2U-Server/src/test/java/com/mentionall/cpr2u/education/repository/FakeLectureRepository.java b/CPR2U-Server/src/test/java/com/mentionall/cpr2u/education/repository/FakeLectureRepository.java deleted file mode 100644 index bc9b7a1..0000000 --- a/CPR2U-Server/src/test/java/com/mentionall/cpr2u/education/repository/FakeLectureRepository.java +++ /dev/null @@ -1,178 +0,0 @@ -package com.mentionall.cpr2u.education.repository; - -import com.mentionall.cpr2u.education.domain.Lecture; -import org.springframework.data.domain.Example; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Sort; -import org.springframework.data.repository.query.FluentQuery; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.function.Function; -import java.util.stream.Collectors; - -public class FakeLectureRepository implements LectureRepository{ - - Map map = new HashMap(); - - @Override - public Boolean existsByStep(int step) { - return null; - } - - @Override - public List findAll() { - return map.values().stream() - .map(l -> (Lecture) l) - .collect(Collectors.toList()); - } - - @Override - public List findAll(Sort sort) { - return null; - } - - @Override - public Page findAll(Pageable pageable) { - return null; - } - - @Override - public List findAllById(Iterable longs) { - return null; - } - - @Override - public long count() { - return 0; - } - - @Override - public void deleteById(Long aLong) { - - } - - @Override - public void delete(Lecture entity) { - - } - - @Override - public void deleteAllById(Iterable longs) { - - } - - @Override - public void deleteAll(Iterable entities) { - - } - - @Override - public void deleteAll() { - - } - - @Override - public S save(S entity) { - map.put(entity.getId(), entity); - return entity; - } - - @Override - public List saveAll(Iterable entities) { - return null; - } - - @Override - public Optional findById(Long aLong) { - return Optional.of((Lecture) map.get(aLong)); - } - - @Override - public boolean existsById(Long aLong) { - return false; - } - - @Override - public void flush() { - - } - - @Override - public S saveAndFlush(S entity) { - return null; - } - - @Override - public List saveAllAndFlush(Iterable entities) { - return null; - } - - @Override - public void deleteAllInBatch(Iterable entities) { - - } - - @Override - public void deleteAllByIdInBatch(Iterable longs) { - - } - - @Override - public void deleteAllInBatch() { - - } - - @Override - public Lecture getOne(Long aLong) { - return null; - } - - @Override - public Lecture getById(Long aLong) { - return null; - } - - @Override - public Lecture getReferenceById(Long aLong) { - return null; - } - - @Override - public Optional findOne(Example example) { - return Optional.empty(); - } - - @Override - public List findAll(Example example) { - return null; - } - - @Override - public List findAll(Example example, Sort sort) { - return null; - } - - @Override - public Page findAll(Example example, Pageable pageable) { - return null; - } - - @Override - public long count(Example example) { - return 0; - } - - @Override - public boolean exists(Example example) { - return false; - } - - @Override - public R findBy(Example example, Function, R> queryFunction) { - return null; - } -} diff --git a/CPR2U-Server/src/test/java/com/mentionall/cpr2u/education/repository/FakeQuizRepository.java b/CPR2U-Server/src/test/java/com/mentionall/cpr2u/education/repository/FakeQuizRepository.java deleted file mode 100644 index 3883277..0000000 --- a/CPR2U-Server/src/test/java/com/mentionall/cpr2u/education/repository/FakeQuizRepository.java +++ /dev/null @@ -1,223 +0,0 @@ -package com.mentionall.cpr2u.education.repository; - -import com.mentionall.cpr2u.education.domain.Lecture; -import com.mentionall.cpr2u.education.domain.Quiz; -import com.querydsl.core.types.OrderSpecifier; -import com.querydsl.core.types.Predicate; -import org.springframework.data.domain.Example; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Sort; -import org.springframework.data.repository.query.FluentQuery; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.function.Function; - -public class FakeQuizRepository implements QuizRepository{ - - Map map = new HashMap(); - - @Override - public List findRandomLimit5() { - return null; - } - - @Override - public List findAll() { - return null; - } - - @Override - public List findAll(Sort sort) { - return null; - } - - @Override - public Page findAll(Pageable pageable) { - return null; - } - - @Override - public List findAllById(Iterable longs) { - return null; - } - - @Override - public long count() { - return 0; - } - - @Override - public void deleteById(Long aLong) { - - } - - @Override - public void delete(Quiz entity) { - - } - - @Override - public void deleteAllById(Iterable longs) { - - } - - @Override - public void deleteAll(Iterable entities) { - - } - - @Override - public void deleteAll() { - - } - - @Override - public S save(S entity) { - map.put(entity.getId(), entity); - return entity; - } - - @Override - public List saveAll(Iterable entities) { - return null; - } - - @Override - public Optional findById(Long aLong) { - return Optional.of((Quiz) map.get(aLong)); - } - - @Override - public boolean existsById(Long aLong) { - return false; - } - - @Override - public void flush() { - - } - - @Override - public S saveAndFlush(S entity) { - return null; - } - - @Override - public List saveAllAndFlush(Iterable entities) { - return null; - } - - @Override - public void deleteAllInBatch(Iterable entities) { - - } - - @Override - public void deleteAllByIdInBatch(Iterable longs) { - - } - - @Override - public void deleteAllInBatch() { - - } - - @Override - public Quiz getOne(Long aLong) { - return null; - } - - @Override - public Quiz getById(Long aLong) { - return null; - } - - @Override - public Quiz getReferenceById(Long aLong) { - return null; - } - - @Override - public Optional findOne(Example example) { - return Optional.empty(); - } - - @Override - public List findAll(Example example) { - return null; - } - - @Override - public List findAll(Example example, Sort sort) { - return null; - } - - @Override - public Page findAll(Example example, Pageable pageable) { - return null; - } - - @Override - public long count(Example example) { - return 0; - } - - @Override - public boolean exists(Example example) { - return false; - } - - @Override - public R findBy(Example example, Function, R> queryFunction) { - return null; - } - - @Override - public Optional findOne(Predicate predicate) { - return Optional.empty(); - } - - @Override - public Iterable findAll(Predicate predicate) { - return null; - } - - @Override - public Iterable findAll(Predicate predicate, Sort sort) { - return null; - } - - @Override - public Iterable findAll(Predicate predicate, OrderSpecifier... orders) { - return null; - } - - @Override - public Iterable findAll(OrderSpecifier... orders) { - return null; - } - - @Override - public Page findAll(Predicate predicate, Pageable pageable) { - return null; - } - - @Override - public long count(Predicate predicate) { - return 0; - } - - @Override - public boolean exists(Predicate predicate) { - return false; - } - - @Override - public R findBy(Predicate predicate, Function, R> queryFunction) { - return null; - } -} diff --git a/CPR2U-Server/src/test/java/com/mentionall/cpr2u/education/service/EducationProgressTest.java b/CPR2U-Server/src/test/java/com/mentionall/cpr2u/education/service/EducationProgressTest.java index 251a332..36495a4 100644 --- a/CPR2U-Server/src/test/java/com/mentionall/cpr2u/education/service/EducationProgressTest.java +++ b/CPR2U-Server/src/test/java/com/mentionall/cpr2u/education/service/EducationProgressTest.java @@ -1,227 +1,294 @@ package com.mentionall.cpr2u.education.service; -import com.mentionall.cpr2u.education.domain.EducationProgress; -import com.mentionall.cpr2u.education.domain.Lecture; -import com.mentionall.cpr2u.education.domain.ProgressStatus; -import com.mentionall.cpr2u.education.dto.EducationProgressDto; -import com.mentionall.cpr2u.education.dto.ScoreDto; -import com.mentionall.cpr2u.education.repository.EducationProgressRepository; -import com.mentionall.cpr2u.education.repository.FakeEducationProgressRepository; -import com.mentionall.cpr2u.education.repository.FakeLectureRepository; -import com.mentionall.cpr2u.education.repository.LectureRepository; -import com.mentionall.cpr2u.user.domain.AngelStatusEnum; +import com.mentionall.cpr2u.education.dto.ScoreRequestDto; +import com.mentionall.cpr2u.education.dto.lecture.LectureRequestDto; +import com.mentionall.cpr2u.user.domain.AngelStatus; import com.mentionall.cpr2u.user.domain.User; -import com.mentionall.cpr2u.user.dto.UserSignUpDto; -import com.mentionall.cpr2u.user.repository.FakeUserRepository; +import com.mentionall.cpr2u.user.dto.user.SignUpRequestDto; import com.mentionall.cpr2u.user.repository.UserRepository; +import com.mentionall.cpr2u.user.service.AddressService; +import com.mentionall.cpr2u.user.service.AuthService; +import com.mentionall.cpr2u.user.service.UserService; import com.mentionall.cpr2u.util.exception.CustomException; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.*; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import javax.transaction.Transactional; import java.time.LocalDate; -import java.util.ArrayList; -import java.util.stream.Collectors; import static com.mentionall.cpr2u.education.domain.ProgressStatus.*; import static com.mentionall.cpr2u.education.domain.TestStandard.*; -import static com.mentionall.cpr2u.education.domain.TestStandard.totalStep; +import static com.mentionall.cpr2u.user.domain.AngelStatus.ACQUIRED; +import static com.mentionall.cpr2u.user.domain.AngelStatus.UNACQUIRED; import static org.assertj.core.api.Assertions.assertThat; @SpringBootTest +@DisplayName("μœ μ € ꡐ윑 진도 κ΄€λ ¨ ν…ŒμŠ€νŠΈ") +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) public class EducationProgressTest { + @Autowired private EducationProgressService progressService; + @Autowired + private UserService userService; + @Autowired + private AuthService authService; + @Autowired private UserRepository userRepository; - private LectureRepository lectureRepository; - private EducationProgressRepository progressRepository; + @Autowired + private LectureService lectureService; + @Autowired + private AddressService addressService; + + private static final String phoneNumber = "010-0000-0000"; @BeforeEach - public void beforeEach() { - userRepository = new FakeUserRepository(); - lectureRepository = new FakeLectureRepository(); - progressRepository = new FakeEducationProgressRepository(); - progressService = new EducationProgressService(progressRepository, lectureRepository); + private void beforeEach() { + addressService.loadAddressList(); + var address = addressService.readAll().get(0).getGugunList().get(0); + authService.signup(new SignUpRequestDto("ν˜„μ• ", phoneNumber, address.getId(), "device_token")); } - @BeforeEach - public void insertData() { + @Test + @Transactional + public void κ°•μ˜_μˆ˜κ°•μ€‘μΈ_경우() { + //given + createLectureCourse(); + User user = userRepository.findByPhoneNumber(phoneNumber).get(); - LocalDate after3Days = LocalDate.now().minusDays(3); - LocalDate after90Days = LocalDate.now().minusDays(90); - LocalDate after91Days = LocalDate.now().minusDays(91); + //when + var lectureList = lectureService.readLectureProgressAndList(user).getLectureList(); + for (var lecture : lectureList) { + progressService.completeLecture(user, lecture.getId()); + if (lecture.getStep() == finalLectureStep) break; + + // then + var educationInfo = progressService.readEducationInfo(user); + assertThat(educationInfo.getIsLectureCompleted()).isEqualTo(InProgress.ordinal()); + + double progressPercent = lecture.getStep() / totalStep; + assertThat(educationInfo.getProgressPercent()).isEqualTo(progressPercent); + } + } - User user1 = userRepository.save(new User("1L", "ν˜„μ• ", "010-0000-0000", after3Days.atStartOfDay(), AngelStatusEnum.ACQUIRED, null, null, null, null, null, null, null)); - User user2 = userRepository.save(new User("2L", "μ˜ˆμ§„", "010-1111-1111", after3Days.atStartOfDay(), AngelStatusEnum.ACQUIRED, null, null, null, null, null, null, null)); - User user3 = userRepository.save(new User("3L", "μ •ν˜„", "010-2222-2222", after90Days.atStartOfDay(), AngelStatusEnum.ACQUIRED, null, null, null, null, null, null, null)); - User user4 = userRepository.save(new User("4L", "μ±„μ˜", "010-3333-3333", after91Days.atStartOfDay(), AngelStatusEnum.ACQUIRED, null, null, null, null, null, null, null)); + @Test + @Transactional + public void κ°•μ˜_μˆ˜κ°•μ™„λ£Œν•œ_경우() { + //given + createLectureCourse(); + User user = userRepository.findByPhoneNumber(phoneNumber).get(); + //when + completeLectureCourse(user); - EducationProgress educationProgress1 = progressRepository.save(new EducationProgress(1L, user1, null, 0, 0)); - EducationProgress educationProgress2 = progressRepository.save(new EducationProgress(2L, user2, null, 0, 0)); - EducationProgress educationProgress3 = progressRepository.save(new EducationProgress(3L, user3, null, 0, 0)); - EducationProgress educationProgress4 = progressRepository.save(new EducationProgress(4L, user4, null, 0, 0)); + //then + var educationInfo = progressService.readEducationInfo(user); + assertThat(educationInfo.getIsLectureCompleted()).isEqualTo(Completed.ordinal()); + assertThat(educationInfo.getProgressPercent()).isEqualTo((double)finalLectureStep / (double)totalStep); - lectureRepository.save(new Lecture(1L, "타이틀", "κ°•μ˜ URL", 1, "μ„€λͺ…", new ArrayList<>())); + assertThat(educationInfo.getIsQuizCompleted()).isEqualTo(NotCompleted.ordinal()); + assertThat(educationInfo.getIsPostureCompleted()).isEqualTo(NotCompleted.ordinal()); } @Test - @DisplayName("κ°•μ˜ 이수 μ™„λ£Œ") - public void completeLecture() { + @Transactional + public void ν€΄μ¦ˆ_100점을_λ„˜μ€_경우() { //given - User user = userRepository.findById("1").get(); - - //when lecture course is not started, - verifyLectureProgress(user, null, NotCompleted); + createLectureCourse(); + User user = userRepository.findByPhoneNumber(phoneNumber).get(); + completeLectureCourse(user); - //when lecture course is in progress, - for (var lecture : lectureRepository.findAll()) { - progressService.completeLecture(user, lecture.getId()); + //when + progressService.completeQuiz(user, new ScoreRequestDto(100)); - int currentStep = progressRepository.findByUser(user).get().getLastLecture().getStep(); - boolean isInProgress = currentStep < finalLectureStep; - if (isInProgress) verifyLectureProgress(user, lecture, InProgress); - } + //then + var quizStatus = progressService.readEducationInfo(user).getIsQuizCompleted(); + assertThat(quizStatus).isEqualTo(Completed.ordinal()); - //when lecture course is completed, - verifyLectureProgress(user, null, Completed); + var postureStatus = progressService.readEducationInfo(user).getIsPostureCompleted(); + assertThat(postureStatus).isEqualTo(NotCompleted.ordinal()); } @Test - @DisplayName("ν€΄μ¦ˆ ν…ŒμŠ€νŠΈ 톡과") - public void completeQuiz() { + @Transactional + public void ν€΄μ¦ˆ_100점을_λ„˜μ§€_μ•Šμ€_경우() { //given - User user = userRepository.findById("1").get(); + createLectureCourse(); + User user = userRepository.findByPhoneNumber(phoneNumber).get(); completeLectureCourse(user); - //when quiz test is not started, - verifyQuizProgress(user, NotCompleted); - - //when the user fails the quiz test, + //when Assertions.assertThrows(CustomException.class, - () -> progressService.completeQuiz(user, new ScoreDto(50))); - verifyQuizProgress(user, NotCompleted); + () -> progressService.completeQuiz(user, new ScoreRequestDto(99))); - //when the user succeeds the quiz test, - progressService.completeQuiz(user, new ScoreDto(100)); - verifyQuizProgress(user, Completed); + //then + var quizStatus = progressService.readEducationInfo(user).getIsQuizCompleted(); + assertThat(quizStatus).isEqualTo(NotCompleted.ordinal()); } @Test - @DisplayName("μžμ„Έμ‹€μŠ΅ ν…ŒμŠ€νŠΈ 톡과") - public void completePosture() { + @Transactional + public void ν€΄μ¦ˆ_κ°•μ˜λ₯Ό_λ§ˆλ¬΄λ¦¬ν•˜μ§€_μ•Šκ³ _ν…ŒμŠ€νŠΈν•œ_경우() { //given - User user = userRepository.findById("1").get(); + createLectureCourse(); + User user = userRepository.findByPhoneNumber(phoneNumber).get(); + + //when, then + Assertions.assertThrows(CustomException.class, + () -> progressService.completeQuiz(user, new ScoreRequestDto(100)) + ); + } + + @Test + @Transactional + public void μžμ„Έμ‹€μŠ΅_80점을_λ„˜μ€_경우() { + //given + User user = userRepository.findByPhoneNumber(phoneNumber).get(); + createLectureCourse(); completeLectureCourse(user); - progressService.completeQuiz(user, new ScoreDto(100)); + progressService.completeQuiz(user, new ScoreRequestDto(100)); - //when a posture test is not started, - verifyPostureProgress(user, NotCompleted); + //when + progressService.completePosture(user, new ScoreRequestDto(81)); - //when the user fails the posture test, - Assertions.assertThrows(CustomException.class, - () -> progressService.completePosture(user, new ScoreDto(79))); - verifyPostureProgress(user, NotCompleted); + //then + int postureStatus = progressService.readEducationInfo(user).getIsPostureCompleted(); + assertThat(postureStatus).isEqualTo(Completed.ordinal()); - //when the user succeeds the posture test, - progressService.completePosture(user, new ScoreDto(81)); - verifyPostureProgress(user, Completed); + double progressPercent = progressService.readEducationInfo(user).getProgressPercent(); + assertThat(progressPercent).isEqualTo(1.0); } @Test @Transactional - @DisplayName("κ°•μ˜λ₯Ό 듣지 μ•ŠμœΌλ©΄ ν€΄μ¦ˆ ν…ŒμŠ€νŠΈ λΆˆν†΅κ³Ό") - public void completeQuizWithoutLecture() { + public void μžμ„Έμ‹€μŠ΅_80점을_λ„˜μ§€μ•Šμ€_경우() { //given - User user = userRepository.findById("1").get(); + User user = userRepository.findByPhoneNumber(phoneNumber).get(); + createLectureCourse(); + completeLectureCourse(user); + progressService.completeQuiz(user, new ScoreRequestDto(100)); - // when the lecture course is not completed, + //when Assertions.assertThrows(CustomException.class, - () -> progressService.completeQuiz(user, new ScoreDto(100)) - ); + () -> progressService.completePosture(user, new ScoreRequestDto(79))); + + //then + int postureStatus = progressService.readEducationInfo(user).getIsPostureCompleted(); + assertThat(postureStatus).isEqualTo(NotCompleted.ordinal()); } @Test - @DisplayName("κ°•μ˜/ν€΄μ¦ˆλ₯Ό λ§ˆλ¬΄λ¦¬ν•˜μ§€ μ•ŠμœΌλ©΄ μžμ„Έ μ‹€μŠ΅ λΆˆν†΅κ³Ό") - public void completePostureWithoutQuizOrLecture() { + @Transactional + public void μžμ„Έμ‹€μŠ΅_κ°•μ˜λ₯Ό_λ§ˆλ¬΄λ¦¬ν•˜μ§€_μ•Šκ³ _ν…ŒμŠ€νŠΈν•œ_경우() { //given - User user = userRepository.findById("1").get(); + createLectureCourse(); + User user = userRepository.findByPhoneNumber(phoneNumber).get(); - // when the lecture course is not completed, + //when, then Assertions.assertThrows(CustomException.class, - () -> progressService.completePosture(user, new ScoreDto(100))); + () -> progressService.completePosture(user, new ScoreRequestDto(100))); + } - // when the lecture course is completed, but quiz test is not + @Test + @Transactional + public void μžμ„Έμ‹€μŠ΅_ν€΄μ¦ˆλ₯Ό_λ§ˆλ¬΄λ¦¬ν•˜μ§€_μ•Šκ³ _ν…ŒμŠ€νŠΈν•œ_경우() { + //given + createLectureCourse(); + User user = userRepository.findByPhoneNumber(phoneNumber).get(); + + //when, then completeLectureCourse(user); Assertions.assertThrows(CustomException.class, - () -> progressService.completePosture(user, new ScoreDto(100))); + () -> progressService.completePosture(user, new ScoreRequestDto(100))); } @Test - @DisplayName("μ—”μ € μœ νš¨κΈ°κ°„ D-DAY κ°’ 확인") - public void checkAngelStatusDDay() { + @Transactional + public void ꡐ윑_수료_μ „_수료증_확인() { + //given + User user = userRepository.findByPhoneNumber(phoneNumber).get(); - //get - User user1 = userRepository.findById("1").get(); - user1.acquireCertification(); - User user2 = userRepository.findById("2").get(); - User user3 = userRepository.findById("3").get(); - User user4 = userRepository.findById("4").get(); + //when + var educationInfo = progressService.readEducationInfo(user); - EducationProgress educationProgress1 = progressRepository.findByUser(user1).get(); - EducationProgress educationProgress2 = progressRepository.findByUser(user2).get(); - EducationProgress educationProgress3 = progressRepository.findByUser(user3).get(); - EducationProgress educationProgress4 = progressRepository.findByUser(user4).get(); + //then + assertThat(educationInfo.getAngelStatus()).isEqualTo(UNACQUIRED.ordinal()); + assertThat(educationInfo.getDaysLeftUntilExpiration()).isEqualTo(null); + } + + //TODO: 당일, 3일, 90일, 91일 수료증 확인 κ³Όμ • ν•˜λ‚˜λ‘œ ν•©μΉ˜κΈ° + @Test + @Transactional + public void ꡐ윑_수료_당일_수료증_확인() { + //given + User user = userRepository.findByPhoneNumber(phoneNumber).get(); + userService.certificate(user, LocalDate.now().atStartOfDay()); //when - EducationProgressDto acquireAngelToday = new EducationProgressDto(educationProgress1, user1); - EducationProgressDto acquireAngelAfter3Day = new EducationProgressDto(educationProgress2, user2); - EducationProgressDto acquireAngelAfter90Day = new EducationProgressDto(educationProgress3, user3); - EducationProgressDto acquireAngelAfter91Day = new EducationProgressDto(educationProgress4, user4); + var educationInfo = progressService.readEducationInfo(user); //then - assertThat(acquireAngelToday.getDaysLeftUntilExpiration()).isEqualTo(90); - assertThat(acquireAngelAfter3Day.getDaysLeftUntilExpiration()).isEqualTo(87); - assertThat(acquireAngelAfter90Day.getDaysLeftUntilExpiration()).isEqualTo(0); - assertThat(acquireAngelAfter91Day.getDaysLeftUntilExpiration()).isEqualTo(null); + assertThat(educationInfo.getAngelStatus()).isEqualTo(ACQUIRED.ordinal()); + assertThat(educationInfo.getDaysLeftUntilExpiration()).isEqualTo(validTime); } - private void verifyLectureProgress(User user, Lecture lecture, ProgressStatus status) { - var progress = progressService.readEducationInfo(user); - assertThat(progress.getIsLectureCompleted()).isEqualTo(status.ordinal()); + @Test + @Transactional + public void ꡐ윑_수료_3일_ν›„_수료증_확인() { + //given + int day = 3; + User user = userRepository.findByPhoneNumber(phoneNumber).get(); + userService.certificate(user, LocalDate.now().minusDays(day).atStartOfDay()); - if (status == InProgress) { - int currentStep = progressRepository.findByUser(user).get().getLastLecture().getStep(); - assertThat(currentStep).isEqualTo(lecture.getStep()); - assertThat(progress.getLastLectureTitle()).isEqualTo(lecture.getTitle()); - } - double progressPercent = - (status == Completed) ? ((double) finalLectureStep / (double) totalStep) : - (status == InProgress) ? (double) lecture.getStep() / (double) totalStep : 0.0; - assertThat(progress.getProgressPercent()).isEqualTo(progressPercent); + //when + var educationInfo = progressService.readEducationInfo(user); + + //then + assertThat(educationInfo.getAngelStatus()).isEqualTo(ACQUIRED.ordinal()); + assertThat(educationInfo.getDaysLeftUntilExpiration()).isEqualTo(validTime - day); } - private void verifyQuizProgress(User user, ProgressStatus status) { - int quizStatus = progressService.readEducationInfo(user).getIsQuizCompleted(); - assertThat(quizStatus).isEqualTo(status.ordinal()); + @Test + @Transactional + public void ꡐ윑_수료_90일_ν›„_수료증_확인() { + //given + int day = 90; + User user = userRepository.findByPhoneNumber(phoneNumber).get(); + userService.certificate(user, LocalDate.now().minusDays(day).atStartOfDay()); + + //when + var educationInfo = progressService.readEducationInfo(user); + + //then + assertThat(educationInfo.getAngelStatus()).isEqualTo(ACQUIRED.ordinal()); + assertThat(educationInfo.getDaysLeftUntilExpiration()).isEqualTo(validTime - day); } - private void verifyPostureProgress(User user, ProgressStatus status) { - var progress = progressService.readEducationInfo(user); - int postureStatus = progress.getIsPostureCompleted(); - assertThat(postureStatus).isEqualTo(status.ordinal()); + @Test + @Transactional + public void ꡐ윑_수료_91일_ν›„_수료증_만료() { + //given + int day = 91; + User user = userRepository.findByPhoneNumber("010-0000-0000").get(); + userService.certificate(user, LocalDate.now().minusDays(day).atStartOfDay()); - if (postureStatus == Completed.ordinal()) - assertThat(progress.getProgressPercent()).isEqualTo(1.0); + //when + var educationInfo = progressService.readEducationInfo(user); + + //then + //assertThat(educationInfo.getAngelStatus()).isEqualTo(EXPIRED); + assertThat(educationInfo.getDaysLeftUntilExpiration()).isEqualTo(null); } private void completeLectureCourse(User user) { - var lectureList = lectureRepository.findAll().stream().sorted().collect(Collectors.toList()); - lectureList.forEach(lecture -> - progressService.completeLecture(user, lecture.getId()) - ); + var lectureList = lectureService.readLectureProgressAndList(user).getLectureList(); + for (var lecture : lectureList) { + progressService.completeLecture(user, lecture.getId()); + } + } + + private void createLectureCourse() { + lectureService.createLecture(new LectureRequestDto(1, "일반인 μ‹¬νμ†Œμƒμˆ  ν‘œμ€€ ꡐ윑", "2020 Korean Guideline", "https://youtu.be/5DWyihalLMM")); } -} +} \ No newline at end of file diff --git a/CPR2U-Server/src/test/java/com/mentionall/cpr2u/education/service/LectureServiceTest.java b/CPR2U-Server/src/test/java/com/mentionall/cpr2u/education/service/LectureServiceTest.java index 01832a8..7748bd0 100644 --- a/CPR2U-Server/src/test/java/com/mentionall/cpr2u/education/service/LectureServiceTest.java +++ b/CPR2U-Server/src/test/java/com/mentionall/cpr2u/education/service/LectureServiceTest.java @@ -1,68 +1,89 @@ package com.mentionall.cpr2u.education.service; -import com.mentionall.cpr2u.education.domain.EducationProgress; -import com.mentionall.cpr2u.education.domain.TestStandard; import com.mentionall.cpr2u.education.dto.lecture.LectureRequestDto; -import com.mentionall.cpr2u.education.dto.lecture.LectureResponseDto; -import com.mentionall.cpr2u.education.dto.lecture.PostureLectureResponseDto; -import com.mentionall.cpr2u.education.repository.EducationProgressRepository; import com.mentionall.cpr2u.user.domain.User; -import com.mentionall.cpr2u.user.dto.UserSignUpDto; +import com.mentionall.cpr2u.user.dto.user.SignUpRequestDto; import com.mentionall.cpr2u.user.repository.UserRepository; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; +import com.mentionall.cpr2u.user.service.AddressService; +import com.mentionall.cpr2u.user.service.AuthService; +import org.junit.jupiter.api.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import javax.transaction.Transactional; -import java.util.UUID; - +import static com.mentionall.cpr2u.education.domain.TestStandard.finalLectureStep; import static org.assertj.core.api.Assertions.assertThat; @SpringBootTest +@DisplayName("κ°•μ˜ κ΄€λ ¨ ν…ŒμŠ€νŠΈ") +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) public class LectureServiceTest { @Autowired - private LectureService lectureService; - + private AuthService authService; @Autowired private UserRepository userRepository; - @Autowired - private EducationProgressRepository progressRepository; + private EducationProgressService progressService; + @Autowired + private AddressService addressService; + @Autowired + private LectureService lectureService; + + private static final String phoneNumber = "010-0000-0000"; + private static final String nickname = "μœ μ €"; + + @BeforeEach + private void beforeEach() { + addressService.loadAddressList(); + } @Test @Transactional - @DisplayName("μ‚¬μš©μžμ˜ κ°•μ˜ 진도 쑰회") - public void readLectureProgress() { + public void κ°•μ˜λ₯Ό_μ΄μˆ˜ν•˜μ§€_μ•Šμ€_μœ μ €κ°€_κ°•μ˜_리슀트_쑰회() { //given - User user = userRepository.save(new User(new UserSignUpDto("ν˜„μ• ", "+821000000000", UUID.randomUUID().toString()))); - progressRepository.save(new EducationProgress(user)); + createLectureCourse(); - lectureService.createLecture(new LectureRequestDto(1, "κ°•μ˜1", "1μž…λ‹ˆλ‹€.", "https://naver.com")); + var address = addressService.readAll().get(0).getGugunList().get(0); + authService.signup(new SignUpRequestDto(nickname, phoneNumber, address.getId(), "device_token")); + User user = userRepository.findByPhoneNumber(phoneNumber).get(); //when - var progressDto = lectureService.readLectureProgress(user); + var lectureInfo = lectureService.readLectureProgressAndList(user); //then - assertThat(progressDto.getCurrentStep()).isEqualTo(0); - assertThat(progressDto.getLectureList().size()).isEqualTo(TestStandard.finalLectureStep); - - int beforeStep = 0; - for (LectureResponseDto lecture : progressDto.getLectureList()) { - assertThat(lecture.getStep()).isGreaterThan(beforeStep); - beforeStep = lecture.getStep(); - } + assertThat(lectureInfo.getCurrentStep()).isEqualTo(0); + assertThat(lectureInfo.getLectureList().size()).isEqualTo(finalLectureStep); } @Test - @DisplayName("μžμ„Έμ‹€μŠ΅ κ°•μ˜ 쑰회") - public void readPostureLecture() { - //given & when - PostureLectureResponseDto postureLecture = lectureService.readPostureLecture(); + @Transactional + public void κ°•μ˜λ₯Ό_μ΄μˆ˜ν•œ_μœ μ €κ°€_κ°•μ˜_리슀트_쑰회() { + //given + createLectureCourse(); + + var address = addressService.readAll().get(0).getGugunList().get(0); + authService.signup(new SignUpRequestDto(nickname, phoneNumber, address.getId(), "device_token")); + User user = userRepository.findByPhoneNumber(phoneNumber).get(); + + completeFirstLecture(user); + + //when + var lectureInfo = lectureService.readLectureProgressAndList(user); //then - assertThat(postureLecture.getVideoUrl()).isEqualTo("https://www.naver.com"); + assertThat(lectureInfo.getCurrentStep()).isEqualTo(1); + assertThat(lectureInfo.getLectureList().size()).isEqualTo(finalLectureStep); + } + + private void completeFirstLecture(User user) { + var lectureInfo = lectureService.readLectureProgressAndList(user); + var lecture = lectureInfo.getLectureList().get(0); + progressService.completeLecture(user, lecture.getId()); + } + + private void createLectureCourse() { + lectureService.createLecture(new LectureRequestDto(1, "일반인 μ‹¬νμ†Œμƒμˆ  ν‘œμ€€ ꡐ윑", "2020 Korean Guideline", "https://youtu.be/5DWyihalLMM")); } } diff --git a/CPR2U-Server/src/test/java/com/mentionall/cpr2u/education/service/QuizServiceTest.java b/CPR2U-Server/src/test/java/com/mentionall/cpr2u/education/service/QuizServiceTest.java index 5d72413..b8121ed 100644 --- a/CPR2U-Server/src/test/java/com/mentionall/cpr2u/education/service/QuizServiceTest.java +++ b/CPR2U-Server/src/test/java/com/mentionall/cpr2u/education/service/QuizServiceTest.java @@ -3,28 +3,28 @@ import com.mentionall.cpr2u.education.dto.quiz.QuizAnswerRequestDto; import com.mentionall.cpr2u.education.dto.quiz.QuizRequestDto; import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -import javax.transaction.Transactional; import java.util.ArrayList; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; @SpringBootTest +@DisplayName("ν€΄μ¦ˆ κ΄€λ ¨ ν…ŒμŠ€νŠΈ") +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) public class QuizServiceTest { @Autowired private QuizService quizService; @Test - @Transactional - @DisplayName("5개의 ν€΄μ¦ˆλ₯Ό λ¬΄μž‘μœ„ 쑰회") - public void readRandom5Quiz() { + public void λ‹€μ„―κ°œμ˜_ν€΄μ¦ˆλ₯Ό_λ¬΄μž‘μœ„_μ‘°νšŒν•œ_경우() { //given - create7OXQuiz(); - create2SelectionQuiz(); + createQuizCourse(); //when var quizList = quizService.readRandom5Quiz(); @@ -33,37 +33,46 @@ public void readRandom5Quiz() { assertThat(quizList.size()).isEqualTo(5); } - private void create2SelectionQuiz() { + //TODO + @Test + public void ν€΄μ¦ˆ_생성() { + + } + + private void createQuizCourse() { List answerList = new ArrayList<>(); - answerList.add(new QuizAnswerRequestDto(true, "ν•œκ΅­")); - answerList.add(new QuizAnswerRequestDto(false, "λ―Έκ΅­")); - answerList.add(new QuizAnswerRequestDto(false, "일본")); - answerList.add(new QuizAnswerRequestDto(false, "호주")); - quizService.createQuiz(new QuizRequestDto("μ—¬κΈ°λŠ” μ–΄λ””?","μ •λ‹΅ 이유", "SELECTION", answerList)); + answerList.add(new QuizAnswerRequestDto(false, "O")); + answerList.add(new QuizAnswerRequestDto(true, "X")); + + quizService.createQuiz(new QuizRequestDto("If the patient's pulse returns during CPR, the patient must be raised.", + "Even if the pulse has returned, the cardiac arrest victim should rest in a lying position on a flat surface.", + "OX", + answerList)); + + quizService.createQuiz(new QuizRequestDto("If the patient's pulse returns during CPR, the patient must be raised.", + "Even if the pulse has returned, the cardiac arrest victim should rest in a lying position on a flat surface.", + "OX", + answerList)); + + quizService.createQuiz(new QuizRequestDto("If the patient's pulse returns during CPR, the patient must be raised.", + "Even if the pulse has returned, the cardiac arrest victim should rest in a lying position on a flat surface.", + "OX", + answerList)); answerList = new ArrayList<>(); - answerList.add(new QuizAnswerRequestDto(false, "Corea")); - answerList.add(new QuizAnswerRequestDto(true, "Korea")); - answerList.add(new QuizAnswerRequestDto(false, "KKorea")); - answerList.add(new QuizAnswerRequestDto(false, "CCorea")); - quizService.createQuiz(new QuizRequestDto("ν•œκ΅­μ€ μ˜μ–΄λ‘œ?", "μ •λ‹΅ 이유", "SELECTION", answerList)); - } + answerList.add(new QuizAnswerRequestDto(false, "Forehead")); + answerList.add(new QuizAnswerRequestDto(false, "Below the left clavicle")); + answerList.add(new QuizAnswerRequestDto(false, "Chest")); + answerList.add(new QuizAnswerRequestDto(true, "Below the right clavicle")); + + quizService.createQuiz(new QuizRequestDto("Select locations to attach pads on an AED.", + "You have to attach AED to both below the right clavicle and left side.", + "SELECTION", + answerList)); - private void create7OXQuiz() { - List oIsAnswer = new ArrayList<>(); - oIsAnswer.add(new QuizAnswerRequestDto(true, "O")); - oIsAnswer.add(new QuizAnswerRequestDto(false, "X")); - - List xIsAnswer = new ArrayList<>(); - xIsAnswer.add(new QuizAnswerRequestDto(false, "O")); - xIsAnswer.add(new QuizAnswerRequestDto(true, "X")); - - quizService.createQuiz(new QuizRequestDto("μ§€κ΅¬λŠ” λ‘₯κΈ€λ‹€.", "μ •λ‹΅ 이유", "OX", oIsAnswer)); - quizService.createQuiz(new QuizRequestDto("κ³ μ–‘μ΄λŠ” κ·€μ—½λ‹€.", "μ •λ‹΅ 이유", "OX", oIsAnswer)); - quizService.createQuiz(new QuizRequestDto("μˆ™λͺ…μ—¬λŒ€λŠ” 일본에 μžˆλ‹€.", "μ •λ‹΅ 이유", "OX", xIsAnswer)); - quizService.createQuiz(new QuizRequestDto("λ°°κ³ ν”„λ‹€.", "μ •λ‹΅ 이유", "OX", xIsAnswer)); - quizService.createQuiz(new QuizRequestDto("μ§ˆλ¬Έμ€ 총 7κ°œμ΄λ‹€.", "μ •λ‹΅ 이유", "OX", oIsAnswer)); - quizService.createQuiz(new QuizRequestDto("CPR2UλŠ” 4κΈ€μžμ΄λ‹€.", "μ •λ‹΅ 이유", "OX", xIsAnswer)); - quizService.createQuiz(new QuizRequestDto("이것은 μž„μ‹œ μ§ˆλ¬Έλ“€μ΄λ‹€.", "μ •λ‹΅ 이유", "OX", oIsAnswer)); + quizService.createQuiz(new QuizRequestDto("Select locations to attach pads on an AED.", + "You have to attach AED to both below the right clavicle and left side.", + "SELECTION", + answerList)); } } diff --git a/CPR2U-Server/src/test/java/com/mentionall/cpr2u/manager/service/ManagerServiceTest.java b/CPR2U-Server/src/test/java/com/mentionall/cpr2u/manager/service/ManagerServiceTest.java new file mode 100644 index 0000000..77721c8 --- /dev/null +++ b/CPR2U-Server/src/test/java/com/mentionall/cpr2u/manager/service/ManagerServiceTest.java @@ -0,0 +1,77 @@ +package com.mentionall.cpr2u.manager.service; + + +import com.mentionall.cpr2u.education.domain.ProgressStatus; +import com.mentionall.cpr2u.education.service.EducationProgressService; +import com.mentionall.cpr2u.manager.ManagerService; +import com.mentionall.cpr2u.user.domain.AngelStatus; +import com.mentionall.cpr2u.user.domain.User; +import com.mentionall.cpr2u.user.dto.user.SignUpRequestDto; +import com.mentionall.cpr2u.user.repository.UserRepository; +import com.mentionall.cpr2u.user.service.AddressService; +import com.mentionall.cpr2u.user.service.AuthService; +import com.mentionall.cpr2u.user.service.UserService; +import com.mentionall.cpr2u.util.fcm.FirebaseCloudMessageUtil; +import org.junit.jupiter.api.*; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; + +import javax.transaction.Transactional; +import java.time.LocalDate; + +import static org.assertj.core.api.Assertions.assertThat; + +@SpringBootTest +@DisplayName("κ΄€λ¦¬μž κ΄€λ ¨ ν…ŒμŠ€νŠΈ") +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +public class ManagerServiceTest { + @Autowired + private ManagerService managerService; + @MockBean + private FirebaseCloudMessageUtil firebaseCloudMessageUtil; + @Autowired + private EducationProgressService progressService; + @Autowired + private UserService userService; + @Autowired + private AuthService authService; + @Autowired + private UserRepository userRepository; + @Autowired + private AddressService addressService; + + private static final String phoneNumber = "010-0000-0000"; + + @BeforeEach + private void beforeEach() { + addressService.loadAddressList(); + var address = addressService.readAll().get(0).getGugunList().get(0); + authService.signup(new SignUpRequestDto("ν˜„μ• ", phoneNumber, address.getId(), "device_token")); + } + @Test + @Transactional + public void 수료증_λ§Œλ£Œμ‹œ_진도_μ΄ˆκΈ°ν™”() { + //given + User user = userRepository.findByPhoneNumber(phoneNumber).get(); + userService.certificate(user, LocalDate.now().minusDays(91).atStartOfDay()); + + //when + managerService.updateAngelStatus(); + + //then + user = userRepository.findByPhoneNumber(phoneNumber).get(); + var educationInfo = progressService.readEducationInfo(user); + + assertThat(educationInfo.getAngelStatus()).isEqualTo(AngelStatus.EXPIRED); + assertThat(educationInfo.getProgressPercent()).isEqualTo(.0); + + assertThat(educationInfo.getIsLectureCompleted()).isEqualTo(ProgressStatus.NotCompleted.ordinal()); + assertThat(educationInfo.getIsQuizCompleted()).isEqualTo(ProgressStatus.NotCompleted.ordinal()); + assertThat(educationInfo.getIsPostureCompleted()).isEqualTo(ProgressStatus.NotCompleted.ordinal()); + } +} diff --git a/CPR2U-Server/src/test/java/com/mentionall/cpr2u/user/repository/FakeAddressRepository.java b/CPR2U-Server/src/test/java/com/mentionall/cpr2u/user/repository/FakeAddressRepository.java deleted file mode 100644 index c6d8593..0000000 --- a/CPR2U-Server/src/test/java/com/mentionall/cpr2u/user/repository/FakeAddressRepository.java +++ /dev/null @@ -1,260 +0,0 @@ -package com.mentionall.cpr2u.user.repository; - -import com.mentionall.cpr2u.user.domain.Address; -import com.querydsl.core.types.OrderSpecifier; -import com.querydsl.core.types.Predicate; -import com.querydsl.jpa.impl.JPAQuery; -import org.springframework.data.domain.Example; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Sort; -import org.springframework.data.repository.query.FluentQuery; - -import java.util.*; -import java.util.function.Function; - -import static com.mentionall.cpr2u.user.domain.QAddress.address; - -public class FakeAddressRepository implements AddressRepository{ - Map map = new HashMap(); - - @Override - public Optional
findByFullAddress(String[] addressList) { - System.out.println(addressList[0]); - List
findAddressList = findAllBySido(addressList[0]); - - for(int i = 1 ; findAddressList.size() > 1 && i <= 2; i ++) { - String sigugun = addressList[i]; - findAddressList = findBySigugun(sigugun); - } - - if(findAddressList.size() < 1) return Optional.empty(); - return Optional.of(findAddressList.get(0)); - - } - - private List
findAllBySido(String sido) { - List
addressList = new ArrayList<>(); - for(Address address : map.values()){ - if(address.getSido().contains(sido)){ - addressList.add(address); - } - } - return addressList; - } - - private List
findBySigugun(String sigugun) { - List
addressList = new ArrayList<>(); - for(Address address : map.values()){ - if(address.getSigugun().equals(sigugun)){ - addressList.add(address); - } - } - return addressList; - } - - @Override - public List findAllSido() { - return null; - } - - @Override - public List
findAll() { - List
addressList = new ArrayList<>(); - for(Address address : map.values()){ - addressList.add(address); - } - return addressList; - } - - @Override - public List
findAll(Sort sort) { - return null; - } - - @Override - public Page
findAll(Pageable pageable) { - return null; - } - - @Override - public List
findAllById(Iterable longs) { - return null; - } - - @Override - public long count() { - return 0; - } - - @Override - public void deleteById(Long aLong) { - - } - - @Override - public void delete(Address entity) { - - } - - @Override - public void deleteAllById(Iterable longs) { - - } - - @Override - public void deleteAll(Iterable entities) { - - } - - @Override - public void deleteAll() { - - } - - @Override - public S save(S entity) { - map.put(entity.getId(), entity); - return entity; - } - - @Override - public List saveAll(Iterable entities) { - return null; - } - - @Override - public Optional
findById(Long aLong) { - return Optional.of((Address) map.get(aLong)); - } - - @Override - public boolean existsById(Long aLong) { - return false; - } - - @Override - public void flush() { - - } - - @Override - public S saveAndFlush(S entity) { - return null; - } - - @Override - public List saveAllAndFlush(Iterable entities) { - return null; - } - - @Override - public void deleteAllInBatch(Iterable
entities) { - - } - - @Override - public void deleteAllByIdInBatch(Iterable longs) { - - } - - @Override - public void deleteAllInBatch() { - - } - - @Override - public Address getOne(Long aLong) { - return null; - } - - @Override - public Address getById(Long aLong) { - return null; - } - - @Override - public Address getReferenceById(Long aLong) { - return null; - } - - @Override - public Optional findOne(Example example) { - return Optional.empty(); - } - - @Override - public List findAll(Example example) { - return null; - } - - @Override - public List findAll(Example example, Sort sort) { - return null; - } - - @Override - public Page findAll(Example example, Pageable pageable) { - return null; - } - - @Override - public long count(Example example) { - return 0; - } - - @Override - public boolean exists(Example example) { - return false; - } - - @Override - public R findBy(Example example, Function, R> queryFunction) { - return null; - } - - @Override - public Optional
findOne(Predicate predicate) { - return Optional.empty(); - } - - @Override - public Iterable
findAll(Predicate predicate) { - return null; - } - - @Override - public Iterable
findAll(Predicate predicate, Sort sort) { - return null; - } - - @Override - public Iterable
findAll(Predicate predicate, OrderSpecifier... orders) { - return null; - } - - @Override - public Iterable
findAll(OrderSpecifier... orders) { - return null; - } - - @Override - public Page
findAll(Predicate predicate, Pageable pageable) { - return null; - } - - @Override - public long count(Predicate predicate) { - return 0; - } - - @Override - public boolean exists(Predicate predicate) { - return false; - } - - @Override - public R findBy(Predicate predicate, Function, R> queryFunction) { - return null; - } -} diff --git a/CPR2U-Server/src/test/java/com/mentionall/cpr2u/user/repository/FakeDeviceTokenRepository.java b/CPR2U-Server/src/test/java/com/mentionall/cpr2u/user/repository/FakeDeviceTokenRepository.java deleted file mode 100644 index 601d8df..0000000 --- a/CPR2U-Server/src/test/java/com/mentionall/cpr2u/user/repository/FakeDeviceTokenRepository.java +++ /dev/null @@ -1,222 +0,0 @@ -package com.mentionall.cpr2u.user.repository; - -import com.mentionall.cpr2u.user.domain.DeviceToken; -import com.querydsl.core.types.OrderSpecifier; -import com.querydsl.core.types.Predicate; -import org.springframework.data.domain.Example; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Sort; -import org.springframework.data.repository.query.FluentQuery; - -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import java.util.function.Function; - -public class FakeDeviceTokenRepository implements DeviceTokenRepository { - @Override - public List findAllDeviceTokenByUserAddress(Long addressId, String userId) { - return new ArrayList<>(); - } - - @Override - public Optional findByUserId(String userId) { - return Optional.empty(); - } - - @Override - public List findAll() { - return null; - } - - @Override - public List findAll(Sort sort) { - return null; - } - - @Override - public Page findAll(Pageable pageable) { - return null; - } - - @Override - public List findAllById(Iterable strings) { - return null; - } - - @Override - public long count() { - return 0; - } - - @Override - public void deleteById(String s) { - - } - - @Override - public void delete(DeviceToken entity) { - - } - - @Override - public void deleteAllById(Iterable strings) { - - } - - @Override - public void deleteAll(Iterable entities) { - - } - - @Override - public void deleteAll() { - - } - - @Override - public S save(S entity) { - return null; - } - - @Override - public List saveAll(Iterable entities) { - return null; - } - - @Override - public Optional findById(String s) { - return Optional.empty(); - } - - @Override - public boolean existsById(String s) { - return false; - } - - @Override - public void flush() { - - } - - @Override - public S saveAndFlush(S entity) { - return null; - } - - @Override - public List saveAllAndFlush(Iterable entities) { - return null; - } - - @Override - public void deleteAllInBatch(Iterable entities) { - - } - - @Override - public void deleteAllByIdInBatch(Iterable strings) { - - } - - @Override - public void deleteAllInBatch() { - - } - - @Override - public DeviceToken getOne(String s) { - return null; - } - - @Override - public DeviceToken getById(String s) { - return null; - } - - @Override - public DeviceToken getReferenceById(String s) { - return null; - } - - @Override - public Optional findOne(Example example) { - return Optional.empty(); - } - - @Override - public List findAll(Example example) { - return null; - } - - @Override - public List findAll(Example example, Sort sort) { - return null; - } - - @Override - public Page findAll(Example example, Pageable pageable) { - return null; - } - - @Override - public long count(Example example) { - return 0; - } - - @Override - public boolean exists(Example example) { - return false; - } - - @Override - public R findBy(Example example, Function, R> queryFunction) { - return null; - } - - @Override - public Optional findOne(Predicate predicate) { - return Optional.empty(); - } - - @Override - public Iterable findAll(Predicate predicate) { - return null; - } - - @Override - public Iterable findAll(Predicate predicate, Sort sort) { - return null; - } - - @Override - public Iterable findAll(Predicate predicate, OrderSpecifier... orders) { - return null; - } - - @Override - public Iterable findAll(OrderSpecifier... orders) { - return null; - } - - @Override - public Page findAll(Predicate predicate, Pageable pageable) { - return null; - } - - @Override - public long count(Predicate predicate) { - return 0; - } - - @Override - public boolean exists(Predicate predicate) { - return false; - } - - @Override - public R findBy(Predicate predicate, Function, R> queryFunction) { - return null; - } -} diff --git a/CPR2U-Server/src/test/java/com/mentionall/cpr2u/user/repository/FakeUserRepository.java b/CPR2U-Server/src/test/java/com/mentionall/cpr2u/user/repository/FakeUserRepository.java deleted file mode 100644 index 48b9dd6..0000000 --- a/CPR2U-Server/src/test/java/com/mentionall/cpr2u/user/repository/FakeUserRepository.java +++ /dev/null @@ -1,203 +0,0 @@ -package com.mentionall.cpr2u.user.repository; - -import com.mentionall.cpr2u.user.domain.User; -import org.springframework.data.domain.Example; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Sort; -import org.springframework.data.repository.query.FluentQuery; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.function.Function; - -public class FakeUserRepository implements UserRepository{ - Map map = new HashMap(); - - @Override - public Optional findByPhoneNumber(String phoneNumber) { - for(User user : map.values()){ - if(user.getPhoneNumber().equals(user.getPhoneNumber())){ - return Optional.of(user); - } - } - return Optional.empty(); - } - - @Override - public Boolean existsByPhoneNumber(String phoneNumber) { - return null; - } - - @Override - public Boolean existsByNickname(String nickname) { - return null; - } - - @Override - public List findAllAngel() { - return null; - } - - @Override - public List findAll() { - return null; - } - - @Override - public List findAll(Sort sort) { - return null; - } - - @Override - public Page findAll(Pageable pageable) { - return null; - } - - @Override - public List findAllById(Iterable strings) { - return null; - } - - @Override - public long count() { - return 0; - } - - @Override - public void deleteById(String s) { - - } - - @Override - public void delete(User entity) { - - } - - @Override - public void deleteAllById(Iterable strings) { - - } - - @Override - public void deleteAll(Iterable entities) { - - } - - @Override - public void deleteAll() { - - } - - @Override - public S save(S entity) { - for(int i = 1 ; i <= map.size() ; i++){ - String id = String.valueOf(i); - User user = map.get(id); - if(user.getPhoneNumber().equals(entity.getPhoneNumber())){ - map.put(id, user); - return entity; - } - } - entity.expireCertificate(); - map.put(String.valueOf(map.size() + 1), entity); - return entity; - } - - @Override - public List saveAll(Iterable entities) { - return null; - } - - @Override - public Optional findById(String s) { - return Optional.of((User) map.get(s)); - } - - @Override - public boolean existsById(String s) { - return false; - } - - @Override - public void flush() { - - } - - @Override - public S saveAndFlush(S entity) { - return null; - } - - @Override - public List saveAllAndFlush(Iterable entities) { - return null; - } - - @Override - public void deleteAllInBatch(Iterable entities) { - - } - - @Override - public void deleteAllByIdInBatch(Iterable strings) { - - } - - @Override - public void deleteAllInBatch() { - - } - - @Override - public User getOne(String s) { - return null; - } - - @Override - public User getById(String s) { - return null; - } - - @Override - public User getReferenceById(String s) { - return null; - } - - @Override - public Optional findOne(Example example) { - return Optional.empty(); - } - - @Override - public List findAll(Example example) { - return null; - } - - @Override - public List findAll(Example example, Sort sort) { - return null; - } - - @Override - public Page findAll(Example example, Pageable pageable) { - return null; - } - - @Override - public long count(Example example) { - return 0; - } - - @Override - public boolean exists(Example example) { - return false; - } - - @Override - public R findBy(Example example, Function, R> queryFunction) { - return null; - } -} diff --git a/CPR2U-Server/src/test/java/com/mentionall/cpr2u/user/service/AddressServiceTest.java b/CPR2U-Server/src/test/java/com/mentionall/cpr2u/user/service/AddressServiceTest.java index 67912df..deb7c1a 100644 --- a/CPR2U-Server/src/test/java/com/mentionall/cpr2u/user/service/AddressServiceTest.java +++ b/CPR2U-Server/src/test/java/com/mentionall/cpr2u/user/service/AddressServiceTest.java @@ -1,15 +1,11 @@ package com.mentionall.cpr2u.user.service; -import com.mentionall.cpr2u.config.security.JwtTokenProvider; -import com.mentionall.cpr2u.user.domain.Address; import com.mentionall.cpr2u.user.domain.User; -import com.mentionall.cpr2u.user.dto.AddressRequestDto; -import com.mentionall.cpr2u.user.dto.AddressResponseDto; -import com.mentionall.cpr2u.user.dto.UserSignUpDto; -import com.mentionall.cpr2u.user.repository.AddressRepository; +import com.mentionall.cpr2u.user.dto.address.AddressRequestDto; +import com.mentionall.cpr2u.user.dto.address.AddressResponseDto; +import com.mentionall.cpr2u.user.dto.user.SignUpRequestDto; import com.mentionall.cpr2u.user.repository.UserRepository; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @@ -19,64 +15,58 @@ import static org.assertj.core.api.Assertions.assertThat; @SpringBootTest +@DisplayName("μ£Όμ†Œμ§€ κ΄€λ ¨ ν…ŒμŠ€νŠΈ") +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) public class AddressServiceTest { - - @Autowired - private JwtTokenProvider jwtTokenProvider; - @Autowired private AddressService addressService; - @Autowired private UserService userService; - + @Autowired + private AuthService authService; @Autowired private UserRepository userRepository; - @Autowired - private AddressRepository addressRepository; + @BeforeEach + public void beforeEach() { + addressService.loadAddressList(); + } @Test - @DisplayName("μ‚¬μš©μžμ˜ μ£Όμ†Œμ§€ μ„€μ •") @Transactional - public void setAddress() { + public void μœ μ €μ˜_μ£Όμ†Œμ§€_μ„€μ •() { //given - String userId = getUserId("ν˜„μ• ", "010-0000-0000", "device-token"); - Address address = new Address("μ„œμšΈνŠΉλ³„μ‹œ", "μš©μ‚°κ΅¬"); - addressRepository.save(address); + List addressList = addressService.readAll(); + var address = addressList.get(0); + var addressDetail = address.getGugunList().get(0); + + authService.signup(new SignUpRequestDto("ν˜„μ• ", "010-0000-0000", addressDetail.getId(), "device-token")); + User user = userRepository.findByPhoneNumber("010-0000-0000").get(); //when - User user = userRepository.findById(userId).get(); - addressService.setAddress(user, new AddressRequestDto(address.getId())); + address = addressList.get(1); + addressDetail = address.getGugunList().get(0); + + addressService.setAddress(user, new AddressRequestDto(addressDetail.getId())); //then - assertThat(user.getAddress().getId()).isEqualTo(address.getId()); - assertThat(user.getAddress().getSido()).isEqualTo("μ„œμšΈνŠΉλ³„μ‹œ"); - assertThat(user.getAddress().getSigugun()).isEqualTo("μš©μ‚°κ΅¬"); + User findUser = userRepository.findByPhoneNumber("010-0000-0000").get(); + assertThat(findUser.getAddress().getSido()).isEqualTo(address.getSido()); + assertThat(findUser.getAddress().getId()).isEqualTo(addressDetail.getId()); + assertThat(findUser.getAddress().getSigugun()).isEqualTo(addressDetail.getGugun()); } @Test - @DisplayName("전체 μ£Όμ†Œμ§€ 리슀트 쑰회") @Transactional - public void readAll() { + public void μ£Όμ†Œμ§€_리슀트_쑰회() { //given - addressRepository.save(new Address("μ„œμšΈνŠΉλ³„μ‹œ", "μš©μ‚°κ΅¬")); - addressRepository.save(new Address("μ„œμšΈνŠΉλ³„μ‹œ", "쀑ꡬ")); - addressRepository.save(new Address("μ„œμšΈνŠΉλ³„μ‹œ", "μ’…λ‘œκ΅¬")); - addressRepository.save(new Address("μ„œμšΈνŠΉλ³„μ‹œ", "마포ꡬ")); //when List response = addressService.readAll(); //then - assertThat(response.size()).isEqualTo(1); - assertThat(response.get(0).getSido()).isEqualTo("μ„œμšΈνŠΉλ³„μ‹œ"); - assertThat(response.get(0).getGugunList().size()).isEqualTo(4); + assertThat(response.size()).isEqualTo(16); // count of sido + assertThat(response.get(0).getSido()).isEqualTo("강원도"); + assertThat(response.get(0).getGugunList().size()).isEqualTo(18); // count of sigugun } - - private String getUserId(String nickname, String phoneNumber, String deviceToken) { - String accessToken = userService.signup(new UserSignUpDto(nickname, phoneNumber, deviceToken)).getAccessToken(); - return jwtTokenProvider.getUserId(accessToken); - } - } diff --git a/CPR2U-Server/src/test/java/com/mentionall/cpr2u/user/service/AuthServiceTest.java b/CPR2U-Server/src/test/java/com/mentionall/cpr2u/user/service/AuthServiceTest.java index ba81cee..902b7ce 100644 --- a/CPR2U-Server/src/test/java/com/mentionall/cpr2u/user/service/AuthServiceTest.java +++ b/CPR2U-Server/src/test/java/com/mentionall/cpr2u/user/service/AuthServiceTest.java @@ -2,120 +2,157 @@ import com.mentionall.cpr2u.config.security.JwtTokenProvider; import com.mentionall.cpr2u.user.domain.User; -import com.mentionall.cpr2u.user.dto.*; +import com.mentionall.cpr2u.user.dto.user.*; import com.mentionall.cpr2u.user.repository.UserRepository; import com.mentionall.cpr2u.util.exception.CustomException; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; +import com.mentionall.cpr2u.util.twilio.TwilioUtil; +import org.junit.jupiter.api.*; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; import javax.transaction.Transactional; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; @SpringBootTest +@DisplayName("둜그인 κ΄€λ ¨ ν…ŒμŠ€νŠΈ") +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) public class AuthServiceTest { - @Autowired - private UserService userService; + private AuthService authService; + + @MockBean + private TwilioUtil twilioUtil; @Autowired private UserRepository userRepository; - @Autowired private JwtTokenProvider jwtTokenProvider; + @Autowired + private AddressService addressService; - private String phoneNumber = "010-0000-0000"; - private String nickname = "μ˜ˆμ§„"; - private String deviceToken = "device-code"; + private static final String phoneNumber = "010-0000-0000"; + private static final String nickname = "μ˜ˆμ§„"; + private static final String deviceToken = "device-code"; + + @BeforeEach + private void beforeEach() { + addressService.loadAddressList(); + } @Test @Transactional - @DisplayName("νšŒμ› κ°€μž…") - public void signup() { + public void νšŒμ›κ°€μž…() { //given - UserSignUpDto userSignUpDto = new UserSignUpDto(nickname, phoneNumber, deviceToken); + var address = addressService.readAll().get(0).getGugunList().get(0); + SignUpRequestDto signUpRequestDto = new SignUpRequestDto(nickname, phoneNumber, address.getId(), deviceToken); //when - UserTokenDto userTokenDto = userService.signup(userSignUpDto); + authService.signup(signUpRequestDto).getAccessToken(); //then User user = userRepository.findByPhoneNumber(phoneNumber).get(); - String userId1 = user.getId(); - String userId2 = jwtTokenProvider.getUserId(userTokenDto.getAccessToken()); - assertThat(userId1).isEqualTo(userId2); + + assertThat(user.getNickname()).isEqualTo(nickname); + assertThat(user.getDeviceToken().getToken()).isEqualTo(deviceToken); + assertThat(user.getEducationProgress()).isNotNull(); + assertThat(user.getAddress().getId()).isEqualTo(address.getId()); } @Test @Transactional - @DisplayName("둜그인") - public void login() { + public void 둜그인() { //given - UserSignUpDto userSignUpDto = new UserSignUpDto(nickname, phoneNumber, deviceToken); - userService.signup(userSignUpDto); - UserLoginDto userLoginDto = new UserLoginDto(phoneNumber, deviceToken); + var address = addressService.readAll().get(0).getGugunList().get(0); + authService.signup(new SignUpRequestDto(nickname, phoneNumber, address.getId(), deviceToken)); //when - UserTokenDto userTokenDto = userService.login(userLoginDto); + var accessToken = authService.login(new LoginRequestDto(phoneNumber, deviceToken)).getAccessToken(); //then - User user = userRepository.findByPhoneNumber(phoneNumber).get(); - String userId1 = user.getId(); - String userId2 = jwtTokenProvider.getUserId(userTokenDto.getAccessToken()); - assertThat(userId1).isEqualTo(userId2); + User findUser = userRepository.findByPhoneNumber(phoneNumber).get(); + assertThat(findUser.getId()).isEqualTo(jwtTokenProvider.getUserId(accessToken)); + assertThat(findUser.getDeviceToken().getToken()).isEqualTo(deviceToken); } @Test @Transactional - @DisplayName("μ „ν™”λ²ˆν˜Έ μΈμ¦μ½”λ“œ 생성") - public void verification(){ + public void μ „ν™”λ²ˆν˜Έ_μΈμ¦μ½”λ“œ_생성(){ //given - UserPhoneNumberDto userPhoneNumberDto = new UserPhoneNumberDto(phoneNumber); + var phoneNumberInfo = new PhoneNumberRequestDto(phoneNumber); for(int i = 0 ; i < 100 ; i ++) { //when - UserCodeDto userCodeDto = userService.getVerificationCode(userPhoneNumberDto); + CodeResponseDto codeResponseDto = authService.getVerificationCode(phoneNumberInfo); //then - assertThat(userCodeDto.getValidationCode().length()).isEqualTo(4); + assertThat(codeResponseDto.getValidationCode().length()).isEqualTo(4); } } @Test @Transactional - @DisplayName("토큰 μž¬λ°œκΈ‰") - public void autoLogin() { + public void μžλ™_둜그인() { //given - UserSignUpDto userSignUpDto = new UserSignUpDto(nickname, phoneNumber, deviceToken); - userService.signup(userSignUpDto); - UserLoginDto userLoginDto = new UserLoginDto(phoneNumber, deviceToken); - UserTokenDto userTokenDto = userService.login(userLoginDto); - String userId = jwtTokenProvider.getUserId(userTokenDto.getAccessToken()); - UserTokenReissueDto userTokenReissueDto = new UserTokenReissueDto(userTokenDto.getRefreshToken()); + var address = addressService.readAll().get(0).getGugunList().get(0); + authService.signup(new SignUpRequestDto(nickname, phoneNumber, address.getId(), deviceToken)); + var tokens = authService.login(new LoginRequestDto(phoneNumber, deviceToken)); //when - UserTokenDto newUserTokenDto = userService.reissueToken(userTokenReissueDto); + var newTokens = authService.reissueToken(new TokenReissueRequestDto(tokens.getRefreshToken())); //then - assertThat(userId).isEqualTo(jwtTokenProvider.getUserId(newUserTokenDto.getAccessToken())); + assertThat(jwtTokenProvider.getUserId(tokens.getAccessToken())) + .isEqualTo(jwtTokenProvider.getUserId(newTokens.getAccessToken())); } @Test @Transactional - @DisplayName("λ‹‰λ„€μž„ 쀑볡 체크") - public void nicknameCheck() { + public void λ‹‰λ„€μž„_μ€‘λ³΅μ²΄ν¬μ‹œ_μ€‘λ³΅λ˜λŠ”_경우() { //given - UserSignUpDto userSignUpDto = new UserSignUpDto(nickname, phoneNumber, deviceToken); - userService.signup(userSignUpDto); + var address = addressService.readAll().get(0).getGugunList().get(0); + authService.signup(new SignUpRequestDto(nickname, phoneNumber, address.getId(), deviceToken)); //when String newNickname = nickname; //then Assertions.assertThrows(CustomException.class, () -> { - userService.checkNicknameDuplicated(newNickname); - }); + authService.checkNicknameDuplicated(newNickname);}); + } + + @Test + @Transactional + public void λ‹‰λ„€μž„_μ€‘λ³΅μ²΄ν¬μ‹œ_μ‚¬μš©κ°€λŠ₯ν•œ_경우() { + //given + var address = addressService.readAll().get(0).getGugunList().get(0); + authService.signup(new SignUpRequestDto(nickname, phoneNumber, address.getId(), deviceToken)); + + //when + String newNickname = "new" + nickname; + + //then + assertDoesNotThrow(() -> authService.checkNicknameDuplicated(newNickname)); + } + + @Test + @Transactional + public void λ‘œκ·Έμ•„μ›ƒ() { + //given + var address = addressService.readAll().get(0).getGugunList().get(0); + authService.signup(new SignUpRequestDto(nickname, phoneNumber, address.getId(), deviceToken)); + User user = userRepository.findByPhoneNumber(phoneNumber).get(); + + //when + authService.logout(user); + + //then + assertThat(user.getRefreshToken().getToken()).isEqualTo("expired"); } } diff --git a/CPR2U-Server/src/test/java/com/mentionall/cpr2u/user/service/UserServiceTest.java b/CPR2U-Server/src/test/java/com/mentionall/cpr2u/user/service/UserServiceTest.java index cbd71ba..c28bd99 100644 --- a/CPR2U-Server/src/test/java/com/mentionall/cpr2u/user/service/UserServiceTest.java +++ b/CPR2U-Server/src/test/java/com/mentionall/cpr2u/user/service/UserServiceTest.java @@ -1,8 +1,55 @@ package com.mentionall.cpr2u.user.service; +import com.mentionall.cpr2u.user.domain.AngelStatus; +import com.mentionall.cpr2u.user.domain.User; +import com.mentionall.cpr2u.user.dto.user.SignUpRequestDto; +import com.mentionall.cpr2u.user.repository.UserRepository; +import org.junit.jupiter.api.*; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; +import javax.transaction.Transactional; +import java.time.LocalDateTime; + +import static org.assertj.core.api.Assertions.assertThat; + @SpringBootTest +@DisplayName("μœ μ € κ΄€λ ¨ ν…ŒμŠ€νŠΈ") +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) public class UserServiceTest { + @Autowired + private AuthService authService; + @Autowired + private UserService userService; + @Autowired + private UserRepository userRepository; + @Autowired + private AddressService addressService; + + private static final String phoneNumber = "010-0000-0000"; + private static final String nickname = "μ˜ˆμ§„"; + private static final String deviceToken = "device-code"; + + @BeforeEach + private void beforeEach() { + addressService.loadAddressList(); + } + + @Test + @Transactional + public void CPR_Angel_수료증_μ–»κΈ°(){ + //given + var address = addressService.readAll().get(0).getGugunList().get(0); + authService.signup(new SignUpRequestDto(nickname, phoneNumber, address.getId(), deviceToken)); + User user = userRepository.findByPhoneNumber(phoneNumber).get(); + + //when + AngelStatus bfStatus = user.getAngelStatus(); + userService.certificate(user, LocalDateTime.now()); + + //then + assertThat(bfStatus).isEqualTo(AngelStatus.UNACQUIRED); + assertThat(user.getAngelStatus()).isEqualTo(AngelStatus.ACQUIRED); + } } diff --git a/CPR2U-Server/src/test/resources/application.yml b/CPR2U-Server/src/test/resources/application.yml deleted file mode 100644 index 238b554..0000000 --- a/CPR2U-Server/src/test/resources/application.yml +++ /dev/null @@ -1,2 +0,0 @@ -spring: - datasource: \ No newline at end of file diff --git a/CPR2U-Server/src/test/resources/config/application.yml b/CPR2U-Server/src/test/resources/config/application.yml new file mode 100644 index 0000000..175e437 --- /dev/null +++ b/CPR2U-Server/src/test/resources/config/application.yml @@ -0,0 +1,34 @@ +spring: + h2: + console: + enabled: true + + jpa: + database-platform: org.hibernate.dialect.MySQL5Dialect + database: h2 + show-sql: true + open-in-view: false + generate-ddl: true + properties: + hibernate: + show_sql: true + format_sql: true + hibernate: + ddl-auto: create + defer-datasource-initialization: true + + datasource: + driver-class-name: org.h2.Driver + url: jdbc:h2:mem:testdb;MODE=MySQL; + username: SA + password: + + sql: + init: + mode: always + + main: + allow-bean-definition-overriding : true + +security: + secret-key: testtesttesttesttest