From 61d5e22ed9f89b16137fa453c1b7edf22578306c Mon Sep 17 00:00:00 2001 From: yulmam Date: Wed, 24 Jul 2024 12:57:18 +0900 Subject: [PATCH 01/32] =?UTF-8?q?feat=20:=20board=20cors=EC=A0=95=EC=B1=85?= =?UTF-8?q?=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/edufocus/edufocus/board/controller/BoardController.java | 1 - 1 file changed, 1 deletion(-) diff --git a/backend/src/main/java/com/edufocus/edufocus/board/controller/BoardController.java b/backend/src/main/java/com/edufocus/edufocus/board/controller/BoardController.java index dd45f9b..a1ead74 100644 --- a/backend/src/main/java/com/edufocus/edufocus/board/controller/BoardController.java +++ b/backend/src/main/java/com/edufocus/edufocus/board/controller/BoardController.java @@ -19,7 +19,6 @@ import java.util.List; */ @RestController @RequestMapping("/board") -@CrossOrigin("*") public class BoardController { private final JWTUtil jwtUtil; From cc935974e71bc65b77156ea1d7e60a0c497258fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A0=95=EB=AF=BC?= Date: Wed, 24 Jul 2024 13:17:44 +0900 Subject: [PATCH 02/32] =?UTF-8?q?feat:=20swagger=20context-path=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/edufocus/edufocus/swagger/SwaggerConfig.java | 1 + backend/src/main/resources/application.properties | 3 +++ 2 files changed, 4 insertions(+) diff --git a/backend/src/main/java/com/edufocus/edufocus/swagger/SwaggerConfig.java b/backend/src/main/java/com/edufocus/edufocus/swagger/SwaggerConfig.java index 7d71ef7..970f7ba 100644 --- a/backend/src/main/java/com/edufocus/edufocus/swagger/SwaggerConfig.java +++ b/backend/src/main/java/com/edufocus/edufocus/swagger/SwaggerConfig.java @@ -15,6 +15,7 @@ public class SwaggerConfig { .info(new Info() .title("에듀포커스 API") .description("") + .version("1.0.0")); } } \ No newline at end of file diff --git a/backend/src/main/resources/application.properties b/backend/src/main/resources/application.properties index 71bd62a..76ad644 100644 --- a/backend/src/main/resources/application.properties +++ b/backend/src/main/resources/application.properties @@ -28,6 +28,9 @@ spring.jpa.hibernate.ddl-auto=create spring.jpa.show-sql=true +springdoc.api-docs.path=/api/v3/api-docs +springdoc.swagger-ui.path=/api/swagger-ui.html + spring.mail.host=smtp.gmail.com spring.mail.port=587 #spring.mail.username=ssafytestpjt From 5fb3dfc452d75fd0eee6a813a372e6762079c7a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A0=95=EB=AF=BC?= Date: Wed, 24 Jul 2024 13:26:14 +0900 Subject: [PATCH 03/32] =?UTF-8?q?feat:=20swagger-ui=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/edufocus/edufocus/user/config/WebConfiguration.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/main/java/com/edufocus/edufocus/user/config/WebConfiguration.java b/backend/src/main/java/com/edufocus/edufocus/user/config/WebConfiguration.java index bed956a..70e3cec 100644 --- a/backend/src/main/java/com/edufocus/edufocus/user/config/WebConfiguration.java +++ b/backend/src/main/java/com/edufocus/edufocus/user/config/WebConfiguration.java @@ -43,6 +43,6 @@ public class WebConfiguration implements WebMvcConfigurer { public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(jwtInterceptor) .addPathPatterns("/**") // 모든 경로에 대해 인터셉터 적용 - .excludePathPatterns("/auth/**", "/board/**", "/user/**","/lecture/**","/qna/**"); // 인증 없이 접근 가능한 경로 설정 + .excludePathPatterns("swagger-ui/**","/auth/**", "/board/**", "/user/**","/lecture/**","/qna/**"); // 인증 없이 접근 가능한 경로 설정 } } \ No newline at end of file From 6d03363a0e71f88829e8ef6ee963ef84e083ecb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A0=95=EB=AF=BC?= Date: Wed, 24 Jul 2024 13:52:31 +0900 Subject: [PATCH 04/32] feat: swagger --- .../com/edufocus/edufocus/user/config/WebConfiguration.java | 6 +++++- backend/src/main/resources/application.properties | 3 +-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/backend/src/main/java/com/edufocus/edufocus/user/config/WebConfiguration.java b/backend/src/main/java/com/edufocus/edufocus/user/config/WebConfiguration.java index 70e3cec..8a1f979 100644 --- a/backend/src/main/java/com/edufocus/edufocus/user/config/WebConfiguration.java +++ b/backend/src/main/java/com/edufocus/edufocus/user/config/WebConfiguration.java @@ -18,6 +18,7 @@ public class WebConfiguration implements WebMvcConfigurer { public WebConfiguration(JWTInterceptor jwtInterceptor) { super(); + this.jwtInterceptor = jwtInterceptor; } @@ -42,7 +43,10 @@ public class WebConfiguration implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(jwtInterceptor) + .addPathPatterns("/**") // 모든 경로에 대해 인터셉터 적용 - .excludePathPatterns("swagger-ui/**","/auth/**", "/board/**", "/user/**","/lecture/**","/qna/**"); // 인증 없이 접근 가능한 경로 설정 + .excludePathPatterns("/v3/api-docs/**","/swagger-resources/**","/webjars/**","/swagger-ui/**","/auth/**", "/board/**", "/user/**","/lecture/**","/qna/**"); // 인증 없이 접근 가능한 경로 설정 + + ///v3/api-docs/**, /swagger-resources/**, /webjars/** } } \ No newline at end of file diff --git a/backend/src/main/resources/application.properties b/backend/src/main/resources/application.properties index 76ad644..37b4488 100644 --- a/backend/src/main/resources/application.properties +++ b/backend/src/main/resources/application.properties @@ -28,8 +28,7 @@ spring.jpa.hibernate.ddl-auto=create spring.jpa.show-sql=true -springdoc.api-docs.path=/api/v3/api-docs -springdoc.swagger-ui.path=/api/swagger-ui.html + spring.mail.host=smtp.gmail.com spring.mail.port=587 From 2f7e8431451cd7463d8e65a5eedb342586c2b1d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A0=95=EB=AF=BC?= Date: Wed, 24 Jul 2024 15:36:42 +0900 Subject: [PATCH 05/32] =?UTF-8?q?feat:=20refreshToken=20cookie=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/edufocus/edufocus/user/controller/UserController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/main/java/com/edufocus/edufocus/user/controller/UserController.java b/backend/src/main/java/com/edufocus/edufocus/user/controller/UserController.java index e185b6c..f27e42d 100644 --- a/backend/src/main/java/com/edufocus/edufocus/user/controller/UserController.java +++ b/backend/src/main/java/com/edufocus/edufocus/user/controller/UserController.java @@ -77,7 +77,7 @@ public class UserController { Cookie refreshCookie = new Cookie("refresh-token", refreshToken); refreshCookie.setPath("/"); refreshCookie.setHttpOnly(true); - refreshCookie.setSecure(true); // HTTPS에서만 전송되도록 설정 + // refreshCookie.setSecure(true); // HTTPS에서만 전송되도록 설정 // refreshCookie.setSameSite(Cookie.SameSite.NONE); // Cross-Origin 요청에 대해 모두 전송 response.addCookie(refreshCookie); From f6e94fd4d310c1c12b9cbf11aeed40aebe7a9bcd Mon Sep 17 00:00:00 2001 From: kgc9007 Date: Wed, 24 Jul 2024 16:08:28 +0900 Subject: [PATCH 06/32] =?UTF-8?q?feat:=20Lecture=20=EC=9D=B4=EB=AF=B8?= =?UTF-8?q?=EC=A7=80=20=EB=93=B1=EB=A1=9D=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lecture/controller/LectureController.java | 16 +++++++---- .../lecture/entity/LectureCreateRequest.java | 2 -- .../lecture/service/LectureService.java | 3 +- .../lecture/service/LectureServiceImpl.java | 28 +++++++++++++++---- 4 files changed, 35 insertions(+), 14 deletions(-) diff --git a/backend/src/main/java/com/edufocus/edufocus/lecture/controller/LectureController.java b/backend/src/main/java/com/edufocus/edufocus/lecture/controller/LectureController.java index 5ba43cd..b1895d5 100644 --- a/backend/src/main/java/com/edufocus/edufocus/lecture/controller/LectureController.java +++ b/backend/src/main/java/com/edufocus/edufocus/lecture/controller/LectureController.java @@ -5,17 +5,19 @@ import com.edufocus.edufocus.lecture.entity.LectureCreateRequest; import com.edufocus.edufocus.lecture.entity.LectureSearchResponse; import com.edufocus.edufocus.lecture.entity.LectureDetailResponse; import com.edufocus.edufocus.lecture.service.LectureService; -import com.edufocus.edufocus.user.model.entity.User; -import com.edufocus.edufocus.user.model.service.UserService; -import com.edufocus.edufocus.user.model.service.UserServiceImpl; import com.edufocus.edufocus.user.util.JWTUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; +import java.io.File; +import java.io.IOException; import java.util.List; +import java.util.UUID; @RestController @RequestMapping("/lecture") @@ -26,8 +28,9 @@ public class LectureController { private final LectureService lectureService; private final JWTUtil jwtUtil; - @PostMapping - public ResponseEntity createLecture(@RequestHeader("Authorization") String accessToken, @RequestBody LectureCreateRequest lectureCreateRequest) { + @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + public ResponseEntity createLecture(@RequestHeader("Authorization") String accessToken, @RequestPart LectureCreateRequest lectureCreateRequest + , @RequestPart(value = "image", required = false) MultipartFile image) throws Exception { Long userId = Long.parseLong(jwtUtil.getUserId(accessToken)); Lecture lecture = lectureService.findLectureByTitle(lectureCreateRequest.getTitle()); @@ -36,7 +39,8 @@ public class LectureController { return new ResponseEntity<>(msg, HttpStatus.CONFLICT); } - lectureService.createLecture(userId, lectureCreateRequest); + lectureService.createLecture(userId, lectureCreateRequest, image); + String msg = new String("Lecture registered successfully"); return new ResponseEntity<>(msg, HttpStatus.CREATED); } diff --git a/backend/src/main/java/com/edufocus/edufocus/lecture/entity/LectureCreateRequest.java b/backend/src/main/java/com/edufocus/edufocus/lecture/entity/LectureCreateRequest.java index 84c2181..d76afbe 100644 --- a/backend/src/main/java/com/edufocus/edufocus/lecture/entity/LectureCreateRequest.java +++ b/backend/src/main/java/com/edufocus/edufocus/lecture/entity/LectureCreateRequest.java @@ -15,8 +15,6 @@ public class LectureCreateRequest { private String description; - private String image; - private Date startDate; private Date endDate; diff --git a/backend/src/main/java/com/edufocus/edufocus/lecture/service/LectureService.java b/backend/src/main/java/com/edufocus/edufocus/lecture/service/LectureService.java index 88fc735..962ed5d 100644 --- a/backend/src/main/java/com/edufocus/edufocus/lecture/service/LectureService.java +++ b/backend/src/main/java/com/edufocus/edufocus/lecture/service/LectureService.java @@ -5,6 +5,7 @@ import com.edufocus.edufocus.lecture.entity.LectureCreateRequest; import com.edufocus.edufocus.lecture.entity.LectureSearchResponse; import com.edufocus.edufocus.lecture.entity.LectureDetailResponse; import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; import java.util.List; @@ -12,7 +13,7 @@ import java.util.List; @Service public interface LectureService { - void createLecture(long userId, LectureCreateRequest lectureCreateRequest); + void createLecture(long userId, LectureCreateRequest lectureCreateRequest, MultipartFile image) throws Exception; boolean updateLecture(long userId, long lectureId, LectureCreateRequest lectureCreateRequest); diff --git a/backend/src/main/java/com/edufocus/edufocus/lecture/service/LectureServiceImpl.java b/backend/src/main/java/com/edufocus/edufocus/lecture/service/LectureServiceImpl.java index 56b33cf..73267b9 100644 --- a/backend/src/main/java/com/edufocus/edufocus/lecture/service/LectureServiceImpl.java +++ b/backend/src/main/java/com/edufocus/edufocus/lecture/service/LectureServiceImpl.java @@ -11,11 +11,17 @@ import com.edufocus.edufocus.user.model.repository.UserRepository; import jakarta.transaction.Transactional; import lombok.Builder; import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; +import java.io.File; +import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Optional; +import java.util.UUID; @Builder @Service @@ -30,19 +36,34 @@ public class LectureServiceImpl implements LectureService { private final RegistrationRepository registrationRepository; @Override - public void createLecture(long userId, LectureCreateRequest lectureCreateRequest) { + public void createLecture(long userId, LectureCreateRequest lectureCreateRequest, MultipartFile image) throws IOException { User user = userRepository.findById(userId).get(); Lecture lecture = new Lecture().builder() .user(user) .title(lectureCreateRequest.getTitle()) .description(lectureCreateRequest.getDescription()) - .image(lectureCreateRequest.getImage()) .startDate(lectureCreateRequest.getStartDate()) .endDate(lectureCreateRequest.getEndDate()) .plan(lectureCreateRequest.getPlan()) .build(); + if (image != null && !image.isEmpty()) { + String uid = UUID.randomUUID().toString(); + + String currentPath = "backend/src/main/resources/images/"; + File checkPathFile = new File(currentPath); + if (!checkPathFile.exists()) { + checkPathFile.mkdirs(); + } + + File savingImage = new File(currentPath + uid + "_" + image.getOriginalFilename()); + image.transferTo(savingImage.toPath()); + String savePath = savingImage.toPath().toString(); + + lecture.setImage(savePath); + } + lectureRepository.save(lecture); } @@ -60,9 +81,6 @@ public class LectureServiceImpl implements LectureService { if (lectureCreateRequest.getDescription() != null) { lecture.setDescription(lectureCreateRequest.getDescription()); } - if (lectureCreateRequest.getImage() != null) { - lecture.setImage(lectureCreateRequest.getImage()); - } if (lectureCreateRequest.getStartDate() != null) { lecture.setStartDate(lectureCreateRequest.getStartDate()); } From 60b875bb3e2b0dae1331fbe4b36a21c6afae5bb9 Mon Sep 17 00:00:00 2001 From: kgc9007 Date: Thu, 25 Jul 2024 13:13:28 +0900 Subject: [PATCH 07/32] =?UTF-8?q?feat:=20quiz=20=EC=82=AC=EC=A7=84=20?= =?UTF-8?q?=EB=93=B1=EB=A1=9D=20=EC=B6=94=EA=B0=80,=20=ED=80=B4=EC=A6=88?= =?UTF-8?q?=20=ED=83=80=EC=9E=85=20=EC=BD=94=EB=93=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...72-6b2b-4fa8-9982-f0b7e87daf95_testimg.jpg | Bin 0 -> 8567 bytes ...1b-5e2d-44bf-af27-4afa52212ae3_testimg.jpg | Bin 0 -> 8567 bytes ...d7-716f-4157-985c-bcfeee6087b3_testimg.jpg | Bin 0 -> 8567 bytes ...0b-2918-416e-9309-f24836a02b5b_testimg.jpg | Bin 0 -> 8567 bytes ...eb-0d6e-4979-8b76-a4f72ca59b9a_testimg.jpg | Bin 0 -> 8567 bytes ...3d-0b4d-4348-b9bf-77d7ef2ee39c_testimg.jpg | Bin 0 -> 8567 bytes ...76-5ec0-47e5-80f4-27d03ce9c825_testimg.jpg | Bin 0 -> 8567 bytes ...c8-a836-4e5a-9e81-877186d6e1a8_testimg.jpg | Bin 0 -> 8567 bytes ...46-a05e-4298-94c3-a8be8cc772d7_testimg.jpg | Bin 0 -> 8567 bytes ...bb-6a27-4cf0-91b6-1d16f75e92a3_testimg.jpg | Bin 0 -> 8567 bytes ...0d-4bbd-4d86-b5ea-5ef2b5bfbc43_testimg.jpg | Bin 0 -> 8567 bytes .../quiz/controller/QuizController.java | 36 ++++++++++++------ .../edufocus/edufocus/quiz/entity/Quiz.java | 5 ++- .../quiz/entity/QuizCreateRequest.java | 4 +- .../edufocus/quiz/entity/QuizType.java | 5 +++ .../quiz/entity/SetCreateRequest.java | 2 - .../quiz/service/QuizServiceImpl.java | 7 ++-- .../quiz/service/QuizSetServiceImpl.java | 2 - .../user/config/WebConfiguration.java | 2 +- 19 files changed, 37 insertions(+), 26 deletions(-) create mode 100644 backend/backend/src/main/resources/images/2030c172-6b2b-4fa8-9982-f0b7e87daf95_testimg.jpg create mode 100644 backend/backend/src/main/resources/images/24cdf21b-5e2d-44bf-af27-4afa52212ae3_testimg.jpg create mode 100644 backend/backend/src/main/resources/images/54d7ded7-716f-4157-985c-bcfeee6087b3_testimg.jpg create mode 100644 backend/backend/src/main/resources/images/6128e50b-2918-416e-9309-f24836a02b5b_testimg.jpg create mode 100644 backend/backend/src/main/resources/images/6e41fdeb-0d6e-4979-8b76-a4f72ca59b9a_testimg.jpg create mode 100644 backend/backend/src/main/resources/images/74f3623d-0b4d-4348-b9bf-77d7ef2ee39c_testimg.jpg create mode 100644 backend/backend/src/main/resources/images/79e7db76-5ec0-47e5-80f4-27d03ce9c825_testimg.jpg create mode 100644 backend/backend/src/main/resources/images/84a572c8-a836-4e5a-9e81-877186d6e1a8_testimg.jpg create mode 100644 backend/backend/src/main/resources/images/ce0a9b46-a05e-4298-94c3-a8be8cc772d7_testimg.jpg create mode 100644 backend/backend/src/main/resources/images/dc007fbb-6a27-4cf0-91b6-1d16f75e92a3_testimg.jpg create mode 100644 backend/backend/src/main/resources/images/f98b230d-4bbd-4d86-b5ea-5ef2b5bfbc43_testimg.jpg create mode 100644 backend/src/main/java/com/edufocus/edufocus/quiz/entity/QuizType.java diff --git a/backend/backend/src/main/resources/images/2030c172-6b2b-4fa8-9982-f0b7e87daf95_testimg.jpg b/backend/backend/src/main/resources/images/2030c172-6b2b-4fa8-9982-f0b7e87daf95_testimg.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c53ac5e358df846b8d20f1928bd0860aa4302f24 GIT binary patch literal 8567 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 createQuizSet(@RequestHeader("Authorization") String accessToken, @RequestBody QuizSetCreateRequest quizSetCreateRequest) { + @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + public ResponseEntity createQuizSet(@RequestHeader("Authorization") String accessToken, @RequestPart QuizSetCreateRequest quizSetCreateRequest + , @RequestPart(value = "image", required = false) MultipartFile image) throws IOException { Long userId = Long.parseLong(jwtUtil.getUserId(accessToken)); String title = quizSetCreateRequest.getTitle(); - String image = quizSetCreateRequest.getImage(); - - SetCreateRequest setCreateRequest = new SetCreateRequest(userId, title, image); + SetCreateRequest setCreateRequest = new SetCreateRequest(userId, title); QuizSet quizSet = quizSetService.createQuizSet(setCreateRequest); + if (image != null && !image.isEmpty()) { + String uid = UUID.randomUUID().toString(); + + String currentPath = "backend/src/main/resources/images/"; + File checkPathFile = new File(currentPath); + if (!checkPathFile.exists()) { + checkPathFile.mkdirs(); + } + + File savingImage = new File(currentPath + uid + "_" + image.getOriginalFilename()); + image.transferTo(savingImage.toPath()); + String savePath = savingImage.toPath().toString(); + + quizSet.setImage(savePath); + } for (QuizCreateRequest quizCreateRequest : quizSetCreateRequest.getQuizzes()) { quizService.createQuiz(quizSet.getId(), quizCreateRequest); diff --git a/backend/src/main/java/com/edufocus/edufocus/quiz/entity/Quiz.java b/backend/src/main/java/com/edufocus/edufocus/quiz/entity/Quiz.java index a113b12..ad429ac 100644 --- a/backend/src/main/java/com/edufocus/edufocus/quiz/entity/Quiz.java +++ b/backend/src/main/java/com/edufocus/edufocus/quiz/entity/Quiz.java @@ -1,5 +1,6 @@ package com.edufocus.edufocus.quiz.entity; +import com.edufocus.edufocus.user.model.entity.UserRole; import com.fasterxml.jackson.annotation.JsonBackReference; import com.fasterxml.jackson.annotation.JsonManagedReference; import jakarta.persistence.*; @@ -31,8 +32,8 @@ public class Quiz { @Column private String answer; - @Column (name = "is_single") - private boolean isSingle; + @Enumerated(EnumType.STRING) + private QuizType quizType; @Column private String image; diff --git a/backend/src/main/java/com/edufocus/edufocus/quiz/entity/QuizCreateRequest.java b/backend/src/main/java/com/edufocus/edufocus/quiz/entity/QuizCreateRequest.java index eb58041..c140c79 100644 --- a/backend/src/main/java/com/edufocus/edufocus/quiz/entity/QuizCreateRequest.java +++ b/backend/src/main/java/com/edufocus/edufocus/quiz/entity/QuizCreateRequest.java @@ -15,9 +15,7 @@ public class QuizCreateRequest { private String answer; - private boolean isSingle; - - private String image; + private String quizType; private String choice1; diff --git a/backend/src/main/java/com/edufocus/edufocus/quiz/entity/QuizType.java b/backend/src/main/java/com/edufocus/edufocus/quiz/entity/QuizType.java new file mode 100644 index 0000000..b58f56c --- /dev/null +++ b/backend/src/main/java/com/edufocus/edufocus/quiz/entity/QuizType.java @@ -0,0 +1,5 @@ +package com.edufocus.edufocus.quiz.entity; + +public enum QuizType { + SINGLE, MULTIPLE +} diff --git a/backend/src/main/java/com/edufocus/edufocus/quiz/entity/SetCreateRequest.java b/backend/src/main/java/com/edufocus/edufocus/quiz/entity/SetCreateRequest.java index a391ceb..83caff8 100644 --- a/backend/src/main/java/com/edufocus/edufocus/quiz/entity/SetCreateRequest.java +++ b/backend/src/main/java/com/edufocus/edufocus/quiz/entity/SetCreateRequest.java @@ -13,6 +13,4 @@ public class SetCreateRequest { private String title; - private String image; - } diff --git a/backend/src/main/java/com/edufocus/edufocus/quiz/service/QuizServiceImpl.java b/backend/src/main/java/com/edufocus/edufocus/quiz/service/QuizServiceImpl.java index b4e0ac9..0412f68 100644 --- a/backend/src/main/java/com/edufocus/edufocus/quiz/service/QuizServiceImpl.java +++ b/backend/src/main/java/com/edufocus/edufocus/quiz/service/QuizServiceImpl.java @@ -3,14 +3,13 @@ package com.edufocus.edufocus.quiz.service; import com.edufocus.edufocus.quiz.entity.Quiz; import com.edufocus.edufocus.quiz.entity.QuizCreateRequest; import com.edufocus.edufocus.quiz.entity.QuizSet; +import com.edufocus.edufocus.quiz.entity.QuizType; import com.edufocus.edufocus.quiz.repository.QuizRepository; import com.edufocus.edufocus.quiz.repository.QuizSetRepository; import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; -import java.util.List; - @Service @Transactional @RequiredArgsConstructor @@ -28,11 +27,11 @@ public class QuizServiceImpl implements QuizService { .title(quizCreateRequest.getTitle()) .description(quizCreateRequest.getDescription()) .answer(quizCreateRequest.getAnswer()) - .image(quizCreateRequest.getImage()) + .quizType(QuizType.valueOf(quizCreateRequest.getQuizType())) .quizSet(quizSet) .build(); - if (!quiz.isSingle()) { + if (!quiz.getQuizType().equals(QuizType.MULTIPLE)) { quiz.setChoice1(quizCreateRequest.getChoice1()); quiz.setChoice2(quizCreateRequest.getChoice2()); quiz.setChoice3(quizCreateRequest.getChoice3()); diff --git a/backend/src/main/java/com/edufocus/edufocus/quiz/service/QuizSetServiceImpl.java b/backend/src/main/java/com/edufocus/edufocus/quiz/service/QuizSetServiceImpl.java index aefdd03..1836ce5 100644 --- a/backend/src/main/java/com/edufocus/edufocus/quiz/service/QuizSetServiceImpl.java +++ b/backend/src/main/java/com/edufocus/edufocus/quiz/service/QuizSetServiceImpl.java @@ -17,7 +17,6 @@ public class QuizSetServiceImpl implements QuizSetService { private final QuizSetRepository quizSetRepository; private final UserRepository userRepository; - private final QuizRepository quizRepository; @Override @@ -29,7 +28,6 @@ public class QuizSetServiceImpl implements QuizSetService { quizSet.setUser(user); quizSet.setTitle(setCreateRequest.getTitle()); - quizSet.setImage(setCreateRequest.getImage()); return quizSetRepository.save(quizSet); } diff --git a/backend/src/main/java/com/edufocus/edufocus/user/config/WebConfiguration.java b/backend/src/main/java/com/edufocus/edufocus/user/config/WebConfiguration.java index 8a1f979..4bc3d83 100644 --- a/backend/src/main/java/com/edufocus/edufocus/user/config/WebConfiguration.java +++ b/backend/src/main/java/com/edufocus/edufocus/user/config/WebConfiguration.java @@ -45,7 +45,7 @@ public class WebConfiguration implements WebMvcConfigurer { registry.addInterceptor(jwtInterceptor) .addPathPatterns("/**") // 모든 경로에 대해 인터셉터 적용 - .excludePathPatterns("/v3/api-docs/**","/swagger-resources/**","/webjars/**","/swagger-ui/**","/auth/**", "/board/**", "/user/**","/lecture/**","/qna/**"); // 인증 없이 접근 가능한 경로 설정 + .excludePathPatterns("/v3/api-docs/**","/swagger-resources/**","/webjars/**","/swagger-ui/**","/auth/**", "/board/**", "/user/**","/lecture/**","/qna/**", "/quiz/**"); // 인증 없이 접근 가능한 경로 설정 ///v3/api-docs/**, /swagger-resources/**, /webjars/** } From 1b709325b7e3340bd384b1a752e42988bdf38b06 Mon Sep 17 00:00:00 2001 From: kgc9007 Date: Thu, 25 Jul 2024 13:17:34 +0900 Subject: [PATCH 08/32] =?UTF-8?q?remove:=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...0c172-6b2b-4fa8-9982-f0b7e87daf95_testimg.jpg | Bin 8567 -> 0 bytes ...df21b-5e2d-44bf-af27-4afa52212ae3_testimg.jpg | Bin 8567 -> 0 bytes ...7ded7-716f-4157-985c-bcfeee6087b3_testimg.jpg | Bin 8567 -> 0 bytes ...8e50b-2918-416e-9309-f24836a02b5b_testimg.jpg | Bin 8567 -> 0 bytes ...1fdeb-0d6e-4979-8b76-a4f72ca59b9a_testimg.jpg | Bin 8567 -> 0 bytes ...3623d-0b4d-4348-b9bf-77d7ef2ee39c_testimg.jpg | Bin 8567 -> 0 bytes ...7db76-5ec0-47e5-80f4-27d03ce9c825_testimg.jpg | Bin 8567 -> 0 bytes ...572c8-a836-4e5a-9e81-877186d6e1a8_testimg.jpg | Bin 8567 -> 0 bytes ...a9b46-a05e-4298-94c3-a8be8cc772d7_testimg.jpg | Bin 8567 -> 0 bytes ...07fbb-6a27-4cf0-91b6-1d16f75e92a3_testimg.jpg | Bin 8567 -> 0 bytes ...b230d-4bbd-4d86-b5ea-5ef2b5bfbc43_testimg.jpg | Bin 8567 -> 0 bytes 11 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 backend/backend/src/main/resources/images/2030c172-6b2b-4fa8-9982-f0b7e87daf95_testimg.jpg delete mode 100644 backend/backend/src/main/resources/images/24cdf21b-5e2d-44bf-af27-4afa52212ae3_testimg.jpg delete mode 100644 backend/backend/src/main/resources/images/54d7ded7-716f-4157-985c-bcfeee6087b3_testimg.jpg delete mode 100644 backend/backend/src/main/resources/images/6128e50b-2918-416e-9309-f24836a02b5b_testimg.jpg delete mode 100644 backend/backend/src/main/resources/images/6e41fdeb-0d6e-4979-8b76-a4f72ca59b9a_testimg.jpg delete mode 100644 backend/backend/src/main/resources/images/74f3623d-0b4d-4348-b9bf-77d7ef2ee39c_testimg.jpg delete mode 100644 backend/backend/src/main/resources/images/79e7db76-5ec0-47e5-80f4-27d03ce9c825_testimg.jpg delete mode 100644 backend/backend/src/main/resources/images/84a572c8-a836-4e5a-9e81-877186d6e1a8_testimg.jpg delete mode 100644 backend/backend/src/main/resources/images/ce0a9b46-a05e-4298-94c3-a8be8cc772d7_testimg.jpg delete mode 100644 backend/backend/src/main/resources/images/dc007fbb-6a27-4cf0-91b6-1d16f75e92a3_testimg.jpg delete mode 100644 backend/backend/src/main/resources/images/f98b230d-4bbd-4d86-b5ea-5ef2b5bfbc43_testimg.jpg diff --git a/backend/backend/src/main/resources/images/2030c172-6b2b-4fa8-9982-f0b7e87daf95_testimg.jpg b/backend/backend/src/main/resources/images/2030c172-6b2b-4fa8-9982-f0b7e87daf95_testimg.jpg deleted file mode 100644 index c53ac5e358df846b8d20f1928bd0860aa4302f24..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8567 zcmeHKX;_-qw#Lj&Gt?SmV&YJ3P1QIc5=ZnVib-xYHW4%qmyZs!Ch)M!ox#t&CCd7(||xC z5^ahi;>{uhLd}4t=4JqU$H>qC6b>D(i$n)u2@t&*L5-d+HV~rc1$Hxc3w1)@z+Q+Z zp*^B6!BEjSlue+Xql2z}q-`WV6ps!M(2c~05XiQX5WTm~ZI%4C*i29Ntx7l!qIY&X zs_s>{uXUY>B(yHr6l{Vrw*cx|*_Z;sU@I$&)4CSsKua@opcxQk0<^KUw6rxh*ZqU^ zl+j3m7~3n(E`P*Q&LDb!Y$_rm!ZZS8N+boD0c~t-%*-v!EG$fv8Ybi@LU=%=34yHt zrw3;=8AZZ|hGU5Y-EEHnNFpU1qNmLCUt7S3y1Bh`{BNs;$8YcJtu{IQ3i_`!{;f6{ z78Qy%yMiVYDI^qHsjmO0v9fmmInbYqfv9(Sp%hZc+kFP2%+MidJem+rRvsbnZAQw> zZQV&&%rC=dv>w1%E_wgEez0Rhd=KyA#e|FpeGAcqGKQ0VP= z+qT$uwjh`PXlv_4LI;EsNiZTYNE(WE%x8Uce1i^K5OA(19Wma3$*|O&std9K%rJ(3oDB=AgDD^?+@F+ zzt!r`eM7-Okdrmo5(xf(Y|WI(1fp!ivEd=;Kfrl5B>WF91iK9o+mHZ4kg^;mfoMzs zB_v$W;T$@E5{|`CLUeBggoZ}xMiB7<1YImaIStoU0+LL@q%PpbF;cAog+{K*{vzz*f-?b;V_*FISv)JbmOH%6LSA}#k`jhNh$0S>)wZ$RKb zlo~2qV!-!+T|2i)IqurCOZnTod)Kbrd-v_xvv<#)ef#%)q@;cO_aE3M<#_Pmfdi`B zbmSeW?ApD1-`;(P_wPS^A;DmztGwq62qluyN- zZ@+K%vOVnCyKlG3j$J!d-qJgJ9nw>-t*~a zdx2*!2dEu6voB2VQO6T?%D3rA(sz%;;TGqvQLOr%zuwqb4xeI* z30lov1XmG)(uGXsaM&>zOpteGdd8BMd<)QMRh37}+KFwgx*25LX*vw2BRsh!_-Jdk z0S9yH!bDz6j0dN(mMj^IIqB}ar=JSG20GR3(#czE?5>u?*{H*_lxUA}Q?~-qZHV>$i@RT6kuqDfU+hdvwpjop#r{GP7Rh zG6vj<%AbL`RA22tOrNGv>K|FTR?&$~eZ}oTlo~o=6v4d=bki_q8(=#y7|Sj`|F=#c zuPZ_lS_XM+9MMaNwm)r0K~#DD7+zSbDrMv~(7&`8Gn%1ilTLE;3K--dF@@zsOY{?$ z3x{%K!uw2cgIK8Pw5yq4b+q2F(Yc>H z!&<||Cx%K!<*;b$#cnXpvnuxvq^fYdp}Y9%`*aSlFmkhxq$A|Tvb{PN>WT+8Yr|?D z&BoK6yrm~5^5j2lM${*yHm`c7-yd{osTfKjw1AFIFxWYZc??c=a(423Jq$PdbHVip zM5E)Abu;2C!8LyZ*rAYV`?8H)s;b$VXInJRlReFvODi-&PWYvHRCi1LV#4~(T(OB5 z&a@CVu8v@(5?DS_%_!mhD7ahy;wul)5 zF^-k6keR`{%+ri2IF-U-r(ig2Lw0s2r|^-Ue!Xs8?47X2(e-{~W9?`ef$>B-=sdZu z1&1<$n;}=4Hai#QZF48*>`ppmL}#jNouImanXe@|=g|&n)%|7&HQ4$W_sCYo2PIy* zo9(l4j}1l;-dFENr+E<`j{}DclUWd0xXAJ2iwVak;@O1zI-~2i^G?;uwH7XLmLb*m zf{kY3@x!%8hfwue9%>HAuE0tC5V8Mci_I6g)h*;0a_@`?J}I}031*ax^o%vC7GG)Z z>;tnt8HkPfF`ylVt7AXx%f3bXZ3Jke5iBf=uEDtv2w!(x0`iMMCDw{8HK}jyWRzW= zDD}>rU=EHSFMzFG;4miWG*va=!$^a0>lwtiwch=k=w8A{P$qc}1e&@kSb5S;k~i@` z>Pl_nNF`eU+;6QTeV@zV4#`h%C?JWXtY?)yLpphB~& zUtaiWGdE_$qMDxvChlD}rfp>1T#VLiJJt`AS0*l0+pYUwYkER6+sE#ztFDmYLM5j! zi$1xwd)IN* z6&MA#@~aH{pu;$@05Z7xc~71ASqUT>9v>HTMlS*D+;X&tb=nO9k9XCFv-NaJ!`fgn z+}(NHVnRtT0Vy34Ip~~4ooR#BOT8Yt50$so<2T}~XZm{k6b@T}F_YtqT{Qdl+lTx5 zb<-fv`6upr3A0%44^UWJcTZ)3Ox&9E1mY_zT!1@ z6&CPy4#VTFg&kjF^M>l>KfJCY)2|ad$2(=>c)Wl17NEPa1I+ra;zrU6{1;?_|IEpo zB0j#J*2nuQs#9uwlrbBP)5>@mHIYwQPXr-)Z~XB1O7$F*=1Ik81t(`0niJ z24B~>R8ix=UvaL_4k3BL6dI7PzGu|9iUygk#90WAI4^zBQsDHbBpH(n?%HK?z>oBtF|NJGtsvh(cdMyvK ze)VZ^&3UTq>DvDW3%`PS0lT#=N9TNU5!d?9Q&}ZjfI+fjFK+97LzRrwV zBI(|^m}=5$@fg`FVR~RTir%o121tT9|CC#-W-h7u(+I!!K_=?b+(zZFW1;`5E3bHr z=KFjjMVf#f*wFs*ah;V2GV|H`jPmBv$eJ(K|LR3e?;gm#>fIH{z;+GbjLhaJ&zD1Rp;&mOE z&tuKVr!<4uh10xgyrbUwjPZ^7*LKX7?y#7U>#v`ey7_;^!8Vl^dyH`?9Ix0Q2)yqv z)ShzS`dcozxQ@*nY=Gcm{vpi_-U7h+J@;-UB~LA`>XataO)Q;V<1H+^R8PxeUT=Iw z!^~tEcF4xl+k<%~xA0hczvx4{K=)KEW{uS8Awy2itc4+GshnoC7`=!nxH#)71Yc=7Q&Y?%j0ig4R0TgM5j7zzjMHA{ z1k;eH!Qkg-V;WwrwP|rLjx(de)P_0@T5CpJasoQ#q#TEWzM^FM#pBe`2Xo_R6poFi z!{WmJs=SL{+Apd~hlf29$4Bd=$+U-VJw#_{Ik!;i1_TO)19=ZDM`6pt-kHb~RTV^! zr)#-6QXgDP2~$nHq6YGd>+x9dTGFvHmo=w{-un1N+8p_);J8MCc2a#goI+^+vPWC^TsdPL(F5r`1McbJs%#zuT6wR*Sq7@W}!Lqb@ATv(=RZd?@2)AR`!l$ip zUU5!)zu6ozBZ7f#yoJaqc>`t*bEf9&Xa#BO z6&QRmEEe@a8>~!Zi*26xHCelQB$i)t*JIW4<&`FWx_-ApJSsTUO?!f8c;c@QU2ezp zO2r%`n30Q7dSHn9x*Sll&2_=cEr8s6vlutZf~_X)p`xejtxknDj4$2dKJBk+S9OC# z80By}^dISa&z^&ca$86`yk}?BHH;^k#Rj2flO+{C9r!k)!sdPnQL5$sdr;r05UxIb z+PC<~QgkMqip$<8K5mJ7xcZduYEu|WQ&h~HDV4{GQ3C&rF>ii4T@7-_-%_OR95u?! zSwB*qMQu06?8uX+EPLS;=Ny3)TYpH{hS+f;;f>CMDOw`vo87Fv9#<6fX7$iyJ#gZ< zY<${adhI}bbfQJ^6H@p4-~s?RfbDbS-%DJa>^ICw^mp+ z?3g>(KQzAy{{#uJ!TbT}T1bP(#8a^+A}5CwFcw#ay$B(^(P5Y^E+Y8&ranZ=UhXqw;R$ z{~;Uf@ulcFLWHnEA2m>(i5IawCt=rp)_v1K)S#9(;~ahB?p1FxPr&mB z1A9_mA6xLmpeu%q^0}UN$ejEbgB)%SHhcan8%Lkud9cQ^&JPLVm}^TX6SBL~v#=Y9 zgV&b(x;D=1jGjykzCY9d{j@K&jZYM4OX%OaKqg8TzI^eR&;s^|2Ip7g_nw~$E-ai+ ze^A+VSR()F(%vtcUYmB^r**a=axe*sV?83xNWqPqrIpWKbx}0N45cf*1@y5g8xNri zx`>^cgKMWUQi{6@2SlnSr-RInt&NB1$km^Ii~%Gr&GtlBYpKvw^Fm>)Tzab(26%52E!g8Tan`K z1P_!(7X)p%Z|vL*Y-YL&W_wI!l^+|7q!-?(6i&5Ml0*%B)zWVOSugTYbEDjIsClfc@XS@03txeU}snN z%rAbg{N_DipI?40EM|10(F!uAL4L`n=f(>beFYkN707DIDK;bRrQeerKjt2>e@iJo z2HKoX9qmu1C&PPC7dRn4>|T+o!@+PLqr-iLR59H_@KhpR2tl?Uv??lhTj5Egz4MVs zZ1QY4yH&uf_Y!DpFrG(lY!uiTbix&@l#!+Gl>DTWX)*uEI^H1Rrl{N6_xJYSzu95e zx-zoqTWfX5krsa|W;Gp`ztq6UH)xqKT^<*U+Y2%&-BDi?rWC(Oo}Je%uN)hb4)<_1 zE2HsOQ`RcDB`HrC96igltj~u^DijK#Y%F%H+b(<{NIEoly`2P6U*UNRs3@Q6&s`%8 zlTME_>uV^vulon!GMktm;rA-18u-B?PrV0h2?xwU`Q)GSVWY0qes{@+E4uy5H&Rlo zBkt~8OMQR2md^GbRg!J`=)9nCDTbU_q!s z$+^KX;3S~5k9Dff3+}I!-Qa;_3OYG98Y{uORf;dO9pY$)RnAf1>}(F}IahXLB$w)M zSS*zE-@aN7dP0a~l-Y$ZZbWw3OVTwAH|FDg;>ybu-;HI6gs_1gy2LBKh~C!@D_?Mb zkdAkdc~jtSF|s9sL{(vD?Kx2nn)MS13s-N?G_!DtDYMgwKMsg)mP%VH2(RGj(IXxR z@ly{?=PuZ)eY&dZu-`psRfKVV>JvC&1c;~@!j_~I_+S`Q+1acE7ls(an=?#8Jp5Iw z6>AA^We|MN_;)uk5Tr=Sf56FJaD_25TJ#+Ug}39&o-4YlQ7j{cyZs!Ch)M!ox#t&CCd7(||xC z5^ahi;>{uhLd}4t=4JqU$H>qC6b>D(i$n)u2@t&*L5-d+HV~rc1$Hxc3w1)@z+Q+Z zp*^B6!BEjSlue+Xql2z}q-`WV6ps!M(2c~05XiQX5WTm~ZI%4C*i29Ntx7l!qIY&X zs_s>{uXUY>B(yHr6l{Vrw*cx|*_Z;sU@I$&)4CSsKua@opcxQk0<^KUw6rxh*ZqU^ zl+j3m7~3n(E`P*Q&LDb!Y$_rm!ZZS8N+boD0c~t-%*-v!EG$fv8Ybi@LU=%=34yHt zrw3;=8AZZ|hGU5Y-EEHnNFpU1qNmLCUt7S3y1Bh`{BNs;$8YcJtu{IQ3i_`!{;f6{ z78Qy%yMiVYDI^qHsjmO0v9fmmInbYqfv9(Sp%hZc+kFP2%+MidJem+rRvsbnZAQw> zZQV&&%rC=dv>w1%E_wgEez0Rhd=KyA#e|FpeGAcqGKQ0VP= z+qT$uwjh`PXlv_4LI;EsNiZTYNE(WE%x8Uce1i^K5OA(19Wma3$*|O&std9K%rJ(3oDB=AgDD^?+@F+ zzt!r`eM7-Okdrmo5(xf(Y|WI(1fp!ivEd=;Kfrl5B>WF91iK9o+mHZ4kg^;mfoMzs zB_v$W;T$@E5{|`CLUeBggoZ}xMiB7<1YImaIStoU0+LL@q%PpbF;cAog+{K*{vzz*f-?b;V_*FISv)JbmOH%6LSA}#k`jhNh$0S>)wZ$RKb zlo~2qV!-!+T|2i)IqurCOZnTod)Kbrd-v_xvv<#)ef#%)q@;cO_aE3M<#_Pmfdi`B zbmSeW?ApD1-`;(P_wPS^A;DmztGwq62qluyN- zZ@+K%vOVnCyKlG3j$J!d-qJgJ9nw>-t*~a zdx2*!2dEu6voB2VQO6T?%D3rA(sz%;;TGqvQLOr%zuwqb4xeI* z30lov1XmG)(uGXsaM&>zOpteGdd8BMd<)QMRh37}+KFwgx*25LX*vw2BRsh!_-Jdk z0S9yH!bDz6j0dN(mMj^IIqB}ar=JSG20GR3(#czE?5>u?*{H*_lxUA}Q?~-qZHV>$i@RT6kuqDfU+hdvwpjop#r{GP7Rh zG6vj<%AbL`RA22tOrNGv>K|FTR?&$~eZ}oTlo~o=6v4d=bki_q8(=#y7|Sj`|F=#c zuPZ_lS_XM+9MMaNwm)r0K~#DD7+zSbDrMv~(7&`8Gn%1ilTLE;3K--dF@@zsOY{?$ z3x{%K!uw2cgIK8Pw5yq4b+q2F(Yc>H z!&<||Cx%K!<*;b$#cnXpvnuxvq^fYdp}Y9%`*aSlFmkhxq$A|Tvb{PN>WT+8Yr|?D z&BoK6yrm~5^5j2lM${*yHm`c7-yd{osTfKjw1AFIFxWYZc??c=a(423Jq$PdbHVip zM5E)Abu;2C!8LyZ*rAYV`?8H)s;b$VXInJRlReFvODi-&PWYvHRCi1LV#4~(T(OB5 z&a@CVu8v@(5?DS_%_!mhD7ahy;wul)5 zF^-k6keR`{%+ri2IF-U-r(ig2Lw0s2r|^-Ue!Xs8?47X2(e-{~W9?`ef$>B-=sdZu z1&1<$n;}=4Hai#QZF48*>`ppmL}#jNouImanXe@|=g|&n)%|7&HQ4$W_sCYo2PIy* zo9(l4j}1l;-dFENr+E<`j{}DclUWd0xXAJ2iwVak;@O1zI-~2i^G?;uwH7XLmLb*m zf{kY3@x!%8hfwue9%>HAuE0tC5V8Mci_I6g)h*;0a_@`?J}I}031*ax^o%vC7GG)Z z>;tnt8HkPfF`ylVt7AXx%f3bXZ3Jke5iBf=uEDtv2w!(x0`iMMCDw{8HK}jyWRzW= zDD}>rU=EHSFMzFG;4miWG*va=!$^a0>lwtiwch=k=w8A{P$qc}1e&@kSb5S;k~i@` z>Pl_nNF`eU+;6QTeV@zV4#`h%C?JWXtY?)yLpphB~& zUtaiWGdE_$qMDxvChlD}rfp>1T#VLiJJt`AS0*l0+pYUwYkER6+sE#ztFDmYLM5j! zi$1xwd)IN* z6&MA#@~aH{pu;$@05Z7xc~71ASqUT>9v>HTMlS*D+;X&tb=nO9k9XCFv-NaJ!`fgn z+}(NHVnRtT0Vy34Ip~~4ooR#BOT8Yt50$so<2T}~XZm{k6b@T}F_YtqT{Qdl+lTx5 zb<-fv`6upr3A0%44^UWJcTZ)3Ox&9E1mY_zT!1@ z6&CPy4#VTFg&kjF^M>l>KfJCY)2|ad$2(=>c)Wl17NEPa1I+ra;zrU6{1;?_|IEpo zB0j#J*2nuQs#9uwlrbBP)5>@mHIYwQPXr-)Z~XB1O7$F*=1Ik81t(`0niJ z24B~>R8ix=UvaL_4k3BL6dI7PzGu|9iUygk#90WAI4^zBQsDHbBpH(n?%HK?z>oBtF|NJGtsvh(cdMyvK ze)VZ^&3UTq>DvDW3%`PS0lT#=N9TNU5!d?9Q&}ZjfI+fjFK+97LzRrwV zBI(|^m}=5$@fg`FVR~RTir%o121tT9|CC#-W-h7u(+I!!K_=?b+(zZFW1;`5E3bHr z=KFjjMVf#f*wFs*ah;V2GV|H`jPmBv$eJ(K|LR3e?;gm#>fIH{z;+GbjLhaJ&zD1Rp;&mOE z&tuKVr!<4uh10xgyrbUwjPZ^7*LKX7?y#7U>#v`ey7_;^!8Vl^dyH`?9Ix0Q2)yqv z)ShzS`dcozxQ@*nY=Gcm{vpi_-U7h+J@;-UB~LA`>XataO)Q;V<1H+^R8PxeUT=Iw z!^~tEcF4xl+k<%~xA0hczvx4{K=)KEW{uS8Awy2itc4+GshnoC7`=!nxH#)71Yc=7Q&Y?%j0ig4R0TgM5j7zzjMHA{ z1k;eH!Qkg-V;WwrwP|rLjx(de)P_0@T5CpJasoQ#q#TEWzM^FM#pBe`2Xo_R6poFi z!{WmJs=SL{+Apd~hlf29$4Bd=$+U-VJw#_{Ik!;i1_TO)19=ZDM`6pt-kHb~RTV^! zr)#-6QXgDP2~$nHq6YGd>+x9dTGFvHmo=w{-un1N+8p_);J8MCc2a#goI+^+vPWC^TsdPL(F5r`1McbJs%#zuT6wR*Sq7@W}!Lqb@ATv(=RZd?@2)AR`!l$ip zUU5!)zu6ozBZ7f#yoJaqc>`t*bEf9&Xa#BO z6&QRmEEe@a8>~!Zi*26xHCelQB$i)t*JIW4<&`FWx_-ApJSsTUO?!f8c;c@QU2ezp zO2r%`n30Q7dSHn9x*Sll&2_=cEr8s6vlutZf~_X)p`xejtxknDj4$2dKJBk+S9OC# z80By}^dISa&z^&ca$86`yk}?BHH;^k#Rj2flO+{C9r!k)!sdPnQL5$sdr;r05UxIb z+PC<~QgkMqip$<8K5mJ7xcZduYEu|WQ&h~HDV4{GQ3C&rF>ii4T@7-_-%_OR95u?! zSwB*qMQu06?8uX+EPLS;=Ny3)TYpH{hS+f;;f>CMDOw`vo87Fv9#<6fX7$iyJ#gZ< zY<${adhI}bbfQJ^6H@p4-~s?RfbDbS-%DJa>^ICw^mp+ z?3g>(KQzAy{{#uJ!TbT}T1bP(#8a^+A}5CwFcw#ay$B(^(P5Y^E+Y8&ranZ=UhXqw;R$ z{~;Uf@ulcFLWHnEA2m>(i5IawCt=rp)_v1K)S#9(;~ahB?p1FxPr&mB z1A9_mA6xLmpeu%q^0}UN$ejEbgB)%SHhcan8%Lkud9cQ^&JPLVm}^TX6SBL~v#=Y9 zgV&b(x;D=1jGjykzCY9d{j@K&jZYM4OX%OaKqg8TzI^eR&;s^|2Ip7g_nw~$E-ai+ ze^A+VSR()F(%vtcUYmB^r**a=axe*sV?83xNWqPqrIpWKbx}0N45cf*1@y5g8xNri zx`>^cgKMWUQi{6@2SlnSr-RInt&NB1$km^Ii~%Gr&GtlBYpKvw^Fm>)Tzab(26%52E!g8Tan`K z1P_!(7X)p%Z|vL*Y-YL&W_wI!l^+|7q!-?(6i&5Ml0*%B)zWVOSugTYbEDjIsClfc@XS@03txeU}snN z%rAbg{N_DipI?40EM|10(F!uAL4L`n=f(>beFYkN707DIDK;bRrQeerKjt2>e@iJo z2HKoX9qmu1C&PPC7dRn4>|T+o!@+PLqr-iLR59H_@KhpR2tl?Uv??lhTj5Egz4MVs zZ1QY4yH&uf_Y!DpFrG(lY!uiTbix&@l#!+Gl>DTWX)*uEI^H1Rrl{N6_xJYSzu95e zx-zoqTWfX5krsa|W;Gp`ztq6UH)xqKT^<*U+Y2%&-BDi?rWC(Oo}Je%uN)hb4)<_1 zE2HsOQ`RcDB`HrC96igltj~u^DijK#Y%F%H+b(<{NIEoly`2P6U*UNRs3@Q6&s`%8 zlTME_>uV^vulon!GMktm;rA-18u-B?PrV0h2?xwU`Q)GSVWY0qes{@+E4uy5H&Rlo zBkt~8OMQR2md^GbRg!J`=)9nCDTbU_q!s z$+^KX;3S~5k9Dff3+}I!-Qa;_3OYG98Y{uORf;dO9pY$)RnAf1>}(F}IahXLB$w)M zSS*zE-@aN7dP0a~l-Y$ZZbWw3OVTwAH|FDg;>ybu-;HI6gs_1gy2LBKh~C!@D_?Mb zkdAkdc~jtSF|s9sL{(vD?Kx2nn)MS13s-N?G_!DtDYMgwKMsg)mP%VH2(RGj(IXxR z@ly{?=PuZ)eY&dZu-`psRfKVV>JvC&1c;~@!j_~I_+S`Q+1acE7ls(an=?#8Jp5Iw z6>AA^We|MN_;)uk5Tr=Sf56FJaD_25TJ#+Ug}39&o-4YlQ7j{cyZs!Ch)M!ox#t&CCd7(||xC z5^ahi;>{uhLd}4t=4JqU$H>qC6b>D(i$n)u2@t&*L5-d+HV~rc1$Hxc3w1)@z+Q+Z zp*^B6!BEjSlue+Xql2z}q-`WV6ps!M(2c~05XiQX5WTm~ZI%4C*i29Ntx7l!qIY&X zs_s>{uXUY>B(yHr6l{Vrw*cx|*_Z;sU@I$&)4CSsKua@opcxQk0<^KUw6rxh*ZqU^ zl+j3m7~3n(E`P*Q&LDb!Y$_rm!ZZS8N+boD0c~t-%*-v!EG$fv8Ybi@LU=%=34yHt zrw3;=8AZZ|hGU5Y-EEHnNFpU1qNmLCUt7S3y1Bh`{BNs;$8YcJtu{IQ3i_`!{;f6{ z78Qy%yMiVYDI^qHsjmO0v9fmmInbYqfv9(Sp%hZc+kFP2%+MidJem+rRvsbnZAQw> zZQV&&%rC=dv>w1%E_wgEez0Rhd=KyA#e|FpeGAcqGKQ0VP= z+qT$uwjh`PXlv_4LI;EsNiZTYNE(WE%x8Uce1i^K5OA(19Wma3$*|O&std9K%rJ(3oDB=AgDD^?+@F+ zzt!r`eM7-Okdrmo5(xf(Y|WI(1fp!ivEd=;Kfrl5B>WF91iK9o+mHZ4kg^;mfoMzs zB_v$W;T$@E5{|`CLUeBggoZ}xMiB7<1YImaIStoU0+LL@q%PpbF;cAog+{K*{vzz*f-?b;V_*FISv)JbmOH%6LSA}#k`jhNh$0S>)wZ$RKb zlo~2qV!-!+T|2i)IqurCOZnTod)Kbrd-v_xvv<#)ef#%)q@;cO_aE3M<#_Pmfdi`B zbmSeW?ApD1-`;(P_wPS^A;DmztGwq62qluyN- zZ@+K%vOVnCyKlG3j$J!d-qJgJ9nw>-t*~a zdx2*!2dEu6voB2VQO6T?%D3rA(sz%;;TGqvQLOr%zuwqb4xeI* z30lov1XmG)(uGXsaM&>zOpteGdd8BMd<)QMRh37}+KFwgx*25LX*vw2BRsh!_-Jdk z0S9yH!bDz6j0dN(mMj^IIqB}ar=JSG20GR3(#czE?5>u?*{H*_lxUA}Q?~-qZHV>$i@RT6kuqDfU+hdvwpjop#r{GP7Rh zG6vj<%AbL`RA22tOrNGv>K|FTR?&$~eZ}oTlo~o=6v4d=bki_q8(=#y7|Sj`|F=#c zuPZ_lS_XM+9MMaNwm)r0K~#DD7+zSbDrMv~(7&`8Gn%1ilTLE;3K--dF@@zsOY{?$ z3x{%K!uw2cgIK8Pw5yq4b+q2F(Yc>H z!&<||Cx%K!<*;b$#cnXpvnuxvq^fYdp}Y9%`*aSlFmkhxq$A|Tvb{PN>WT+8Yr|?D z&BoK6yrm~5^5j2lM${*yHm`c7-yd{osTfKjw1AFIFxWYZc??c=a(423Jq$PdbHVip zM5E)Abu;2C!8LyZ*rAYV`?8H)s;b$VXInJRlReFvODi-&PWYvHRCi1LV#4~(T(OB5 z&a@CVu8v@(5?DS_%_!mhD7ahy;wul)5 zF^-k6keR`{%+ri2IF-U-r(ig2Lw0s2r|^-Ue!Xs8?47X2(e-{~W9?`ef$>B-=sdZu z1&1<$n;}=4Hai#QZF48*>`ppmL}#jNouImanXe@|=g|&n)%|7&HQ4$W_sCYo2PIy* zo9(l4j}1l;-dFENr+E<`j{}DclUWd0xXAJ2iwVak;@O1zI-~2i^G?;uwH7XLmLb*m zf{kY3@x!%8hfwue9%>HAuE0tC5V8Mci_I6g)h*;0a_@`?J}I}031*ax^o%vC7GG)Z z>;tnt8HkPfF`ylVt7AXx%f3bXZ3Jke5iBf=uEDtv2w!(x0`iMMCDw{8HK}jyWRzW= zDD}>rU=EHSFMzFG;4miWG*va=!$^a0>lwtiwch=k=w8A{P$qc}1e&@kSb5S;k~i@` z>Pl_nNF`eU+;6QTeV@zV4#`h%C?JWXtY?)yLpphB~& zUtaiWGdE_$qMDxvChlD}rfp>1T#VLiJJt`AS0*l0+pYUwYkER6+sE#ztFDmYLM5j! zi$1xwd)IN* z6&MA#@~aH{pu;$@05Z7xc~71ASqUT>9v>HTMlS*D+;X&tb=nO9k9XCFv-NaJ!`fgn z+}(NHVnRtT0Vy34Ip~~4ooR#BOT8Yt50$so<2T}~XZm{k6b@T}F_YtqT{Qdl+lTx5 zb<-fv`6upr3A0%44^UWJcTZ)3Ox&9E1mY_zT!1@ z6&CPy4#VTFg&kjF^M>l>KfJCY)2|ad$2(=>c)Wl17NEPa1I+ra;zrU6{1;?_|IEpo zB0j#J*2nuQs#9uwlrbBP)5>@mHIYwQPXr-)Z~XB1O7$F*=1Ik81t(`0niJ z24B~>R8ix=UvaL_4k3BL6dI7PzGu|9iUygk#90WAI4^zBQsDHbBpH(n?%HK?z>oBtF|NJGtsvh(cdMyvK ze)VZ^&3UTq>DvDW3%`PS0lT#=N9TNU5!d?9Q&}ZjfI+fjFK+97LzRrwV zBI(|^m}=5$@fg`FVR~RTir%o121tT9|CC#-W-h7u(+I!!K_=?b+(zZFW1;`5E3bHr z=KFjjMVf#f*wFs*ah;V2GV|H`jPmBv$eJ(K|LR3e?;gm#>fIH{z;+GbjLhaJ&zD1Rp;&mOE z&tuKVr!<4uh10xgyrbUwjPZ^7*LKX7?y#7U>#v`ey7_;^!8Vl^dyH`?9Ix0Q2)yqv z)ShzS`dcozxQ@*nY=Gcm{vpi_-U7h+J@;-UB~LA`>XataO)Q;V<1H+^R8PxeUT=Iw z!^~tEcF4xl+k<%~xA0hczvx4{K=)KEW{uS8Awy2itc4+GshnoC7`=!nxH#)71Yc=7Q&Y?%j0ig4R0TgM5j7zzjMHA{ z1k;eH!Qkg-V;WwrwP|rLjx(de)P_0@T5CpJasoQ#q#TEWzM^FM#pBe`2Xo_R6poFi z!{WmJs=SL{+Apd~hlf29$4Bd=$+U-VJw#_{Ik!;i1_TO)19=ZDM`6pt-kHb~RTV^! zr)#-6QXgDP2~$nHq6YGd>+x9dTGFvHmo=w{-un1N+8p_);J8MCc2a#goI+^+vPWC^TsdPL(F5r`1McbJs%#zuT6wR*Sq7@W}!Lqb@ATv(=RZd?@2)AR`!l$ip zUU5!)zu6ozBZ7f#yoJaqc>`t*bEf9&Xa#BO z6&QRmEEe@a8>~!Zi*26xHCelQB$i)t*JIW4<&`FWx_-ApJSsTUO?!f8c;c@QU2ezp zO2r%`n30Q7dSHn9x*Sll&2_=cEr8s6vlutZf~_X)p`xejtxknDj4$2dKJBk+S9OC# z80By}^dISa&z^&ca$86`yk}?BHH;^k#Rj2flO+{C9r!k)!sdPnQL5$sdr;r05UxIb z+PC<~QgkMqip$<8K5mJ7xcZduYEu|WQ&h~HDV4{GQ3C&rF>ii4T@7-_-%_OR95u?! zSwB*qMQu06?8uX+EPLS;=Ny3)TYpH{hS+f;;f>CMDOw`vo87Fv9#<6fX7$iyJ#gZ< zY<${adhI}bbfQJ^6H@p4-~s?RfbDbS-%DJa>^ICw^mp+ z?3g>(KQzAy{{#uJ!TbT}T1bP(#8a^+A}5CwFcw#ay$B(^(P5Y^E+Y8&ranZ=UhXqw;R$ z{~;Uf@ulcFLWHnEA2m>(i5IawCt=rp)_v1K)S#9(;~ahB?p1FxPr&mB z1A9_mA6xLmpeu%q^0}UN$ejEbgB)%SHhcan8%Lkud9cQ^&JPLVm}^TX6SBL~v#=Y9 zgV&b(x;D=1jGjykzCY9d{j@K&jZYM4OX%OaKqg8TzI^eR&;s^|2Ip7g_nw~$E-ai+ ze^A+VSR()F(%vtcUYmB^r**a=axe*sV?83xNWqPqrIpWKbx}0N45cf*1@y5g8xNri zx`>^cgKMWUQi{6@2SlnSr-RInt&NB1$km^Ii~%Gr&GtlBYpKvw^Fm>)Tzab(26%52E!g8Tan`K z1P_!(7X)p%Z|vL*Y-YL&W_wI!l^+|7q!-?(6i&5Ml0*%B)zWVOSugTYbEDjIsClfc@XS@03txeU}snN z%rAbg{N_DipI?40EM|10(F!uAL4L`n=f(>beFYkN707DIDK;bRrQeerKjt2>e@iJo z2HKoX9qmu1C&PPC7dRn4>|T+o!@+PLqr-iLR59H_@KhpR2tl?Uv??lhTj5Egz4MVs zZ1QY4yH&uf_Y!DpFrG(lY!uiTbix&@l#!+Gl>DTWX)*uEI^H1Rrl{N6_xJYSzu95e zx-zoqTWfX5krsa|W;Gp`ztq6UH)xqKT^<*U+Y2%&-BDi?rWC(Oo}Je%uN)hb4)<_1 zE2HsOQ`RcDB`HrC96igltj~u^DijK#Y%F%H+b(<{NIEoly`2P6U*UNRs3@Q6&s`%8 zlTME_>uV^vulon!GMktm;rA-18u-B?PrV0h2?xwU`Q)GSVWY0qes{@+E4uy5H&Rlo zBkt~8OMQR2md^GbRg!J`=)9nCDTbU_q!s z$+^KX;3S~5k9Dff3+}I!-Qa;_3OYG98Y{uORf;dO9pY$)RnAf1>}(F}IahXLB$w)M zSS*zE-@aN7dP0a~l-Y$ZZbWw3OVTwAH|FDg;>ybu-;HI6gs_1gy2LBKh~C!@D_?Mb zkdAkdc~jtSF|s9sL{(vD?Kx2nn)MS13s-N?G_!DtDYMgwKMsg)mP%VH2(RGj(IXxR z@ly{?=PuZ)eY&dZu-`psRfKVV>JvC&1c;~@!j_~I_+S`Q+1acE7ls(an=?#8Jp5Iw z6>AA^We|MN_;)uk5Tr=Sf56FJaD_25TJ#+Ug}39&o-4YlQ7j{cyZs!Ch)M!ox#t&CCd7(||xC z5^ahi;>{uhLd}4t=4JqU$H>qC6b>D(i$n)u2@t&*L5-d+HV~rc1$Hxc3w1)@z+Q+Z zp*^B6!BEjSlue+Xql2z}q-`WV6ps!M(2c~05XiQX5WTm~ZI%4C*i29Ntx7l!qIY&X zs_s>{uXUY>B(yHr6l{Vrw*cx|*_Z;sU@I$&)4CSsKua@opcxQk0<^KUw6rxh*ZqU^ zl+j3m7~3n(E`P*Q&LDb!Y$_rm!ZZS8N+boD0c~t-%*-v!EG$fv8Ybi@LU=%=34yHt zrw3;=8AZZ|hGU5Y-EEHnNFpU1qNmLCUt7S3y1Bh`{BNs;$8YcJtu{IQ3i_`!{;f6{ z78Qy%yMiVYDI^qHsjmO0v9fmmInbYqfv9(Sp%hZc+kFP2%+MidJem+rRvsbnZAQw> zZQV&&%rC=dv>w1%E_wgEez0Rhd=KyA#e|FpeGAcqGKQ0VP= z+qT$uwjh`PXlv_4LI;EsNiZTYNE(WE%x8Uce1i^K5OA(19Wma3$*|O&std9K%rJ(3oDB=AgDD^?+@F+ zzt!r`eM7-Okdrmo5(xf(Y|WI(1fp!ivEd=;Kfrl5B>WF91iK9o+mHZ4kg^;mfoMzs zB_v$W;T$@E5{|`CLUeBggoZ}xMiB7<1YImaIStoU0+LL@q%PpbF;cAog+{K*{vzz*f-?b;V_*FISv)JbmOH%6LSA}#k`jhNh$0S>)wZ$RKb zlo~2qV!-!+T|2i)IqurCOZnTod)Kbrd-v_xvv<#)ef#%)q@;cO_aE3M<#_Pmfdi`B zbmSeW?ApD1-`;(P_wPS^A;DmztGwq62qluyN- zZ@+K%vOVnCyKlG3j$J!d-qJgJ9nw>-t*~a zdx2*!2dEu6voB2VQO6T?%D3rA(sz%;;TGqvQLOr%zuwqb4xeI* z30lov1XmG)(uGXsaM&>zOpteGdd8BMd<)QMRh37}+KFwgx*25LX*vw2BRsh!_-Jdk z0S9yH!bDz6j0dN(mMj^IIqB}ar=JSG20GR3(#czE?5>u?*{H*_lxUA}Q?~-qZHV>$i@RT6kuqDfU+hdvwpjop#r{GP7Rh zG6vj<%AbL`RA22tOrNGv>K|FTR?&$~eZ}oTlo~o=6v4d=bki_q8(=#y7|Sj`|F=#c zuPZ_lS_XM+9MMaNwm)r0K~#DD7+zSbDrMv~(7&`8Gn%1ilTLE;3K--dF@@zsOY{?$ z3x{%K!uw2cgIK8Pw5yq4b+q2F(Yc>H z!&<||Cx%K!<*;b$#cnXpvnuxvq^fYdp}Y9%`*aSlFmkhxq$A|Tvb{PN>WT+8Yr|?D z&BoK6yrm~5^5j2lM${*yHm`c7-yd{osTfKjw1AFIFxWYZc??c=a(423Jq$PdbHVip zM5E)Abu;2C!8LyZ*rAYV`?8H)s;b$VXInJRlReFvODi-&PWYvHRCi1LV#4~(T(OB5 z&a@CVu8v@(5?DS_%_!mhD7ahy;wul)5 zF^-k6keR`{%+ri2IF-U-r(ig2Lw0s2r|^-Ue!Xs8?47X2(e-{~W9?`ef$>B-=sdZu z1&1<$n;}=4Hai#QZF48*>`ppmL}#jNouImanXe@|=g|&n)%|7&HQ4$W_sCYo2PIy* zo9(l4j}1l;-dFENr+E<`j{}DclUWd0xXAJ2iwVak;@O1zI-~2i^G?;uwH7XLmLb*m zf{kY3@x!%8hfwue9%>HAuE0tC5V8Mci_I6g)h*;0a_@`?J}I}031*ax^o%vC7GG)Z z>;tnt8HkPfF`ylVt7AXx%f3bXZ3Jke5iBf=uEDtv2w!(x0`iMMCDw{8HK}jyWRzW= zDD}>rU=EHSFMzFG;4miWG*va=!$^a0>lwtiwch=k=w8A{P$qc}1e&@kSb5S;k~i@` z>Pl_nNF`eU+;6QTeV@zV4#`h%C?JWXtY?)yLpphB~& zUtaiWGdE_$qMDxvChlD}rfp>1T#VLiJJt`AS0*l0+pYUwYkER6+sE#ztFDmYLM5j! zi$1xwd)IN* z6&MA#@~aH{pu;$@05Z7xc~71ASqUT>9v>HTMlS*D+;X&tb=nO9k9XCFv-NaJ!`fgn z+}(NHVnRtT0Vy34Ip~~4ooR#BOT8Yt50$so<2T}~XZm{k6b@T}F_YtqT{Qdl+lTx5 zb<-fv`6upr3A0%44^UWJcTZ)3Ox&9E1mY_zT!1@ z6&CPy4#VTFg&kjF^M>l>KfJCY)2|ad$2(=>c)Wl17NEPa1I+ra;zrU6{1;?_|IEpo zB0j#J*2nuQs#9uwlrbBP)5>@mHIYwQPXr-)Z~XB1O7$F*=1Ik81t(`0niJ z24B~>R8ix=UvaL_4k3BL6dI7PzGu|9iUygk#90WAI4^zBQsDHbBpH(n?%HK?z>oBtF|NJGtsvh(cdMyvK ze)VZ^&3UTq>DvDW3%`PS0lT#=N9TNU5!d?9Q&}ZjfI+fjFK+97LzRrwV zBI(|^m}=5$@fg`FVR~RTir%o121tT9|CC#-W-h7u(+I!!K_=?b+(zZFW1;`5E3bHr z=KFjjMVf#f*wFs*ah;V2GV|H`jPmBv$eJ(K|LR3e?;gm#>fIH{z;+GbjLhaJ&zD1Rp;&mOE z&tuKVr!<4uh10xgyrbUwjPZ^7*LKX7?y#7U>#v`ey7_;^!8Vl^dyH`?9Ix0Q2)yqv z)ShzS`dcozxQ@*nY=Gcm{vpi_-U7h+J@;-UB~LA`>XataO)Q;V<1H+^R8PxeUT=Iw z!^~tEcF4xl+k<%~xA0hczvx4{K=)KEW{uS8Awy2itc4+GshnoC7`=!nxH#)71Yc=7Q&Y?%j0ig4R0TgM5j7zzjMHA{ z1k;eH!Qkg-V;WwrwP|rLjx(de)P_0@T5CpJasoQ#q#TEWzM^FM#pBe`2Xo_R6poFi z!{WmJs=SL{+Apd~hlf29$4Bd=$+U-VJw#_{Ik!;i1_TO)19=ZDM`6pt-kHb~RTV^! zr)#-6QXgDP2~$nHq6YGd>+x9dTGFvHmo=w{-un1N+8p_);J8MCc2a#goI+^+vPWC^TsdPL(F5r`1McbJs%#zuT6wR*Sq7@W}!Lqb@ATv(=RZd?@2)AR`!l$ip zUU5!)zu6ozBZ7f#yoJaqc>`t*bEf9&Xa#BO z6&QRmEEe@a8>~!Zi*26xHCelQB$i)t*JIW4<&`FWx_-ApJSsTUO?!f8c;c@QU2ezp zO2r%`n30Q7dSHn9x*Sll&2_=cEr8s6vlutZf~_X)p`xejtxknDj4$2dKJBk+S9OC# z80By}^dISa&z^&ca$86`yk}?BHH;^k#Rj2flO+{C9r!k)!sdPnQL5$sdr;r05UxIb z+PC<~QgkMqip$<8K5mJ7xcZduYEu|WQ&h~HDV4{GQ3C&rF>ii4T@7-_-%_OR95u?! zSwB*qMQu06?8uX+EPLS;=Ny3)TYpH{hS+f;;f>CMDOw`vo87Fv9#<6fX7$iyJ#gZ< zY<${adhI}bbfQJ^6H@p4-~s?RfbDbS-%DJa>^ICw^mp+ z?3g>(KQzAy{{#uJ!TbT}T1bP(#8a^+A}5CwFcw#ay$B(^(P5Y^E+Y8&ranZ=UhXqw;R$ z{~;Uf@ulcFLWHnEA2m>(i5IawCt=rp)_v1K)S#9(;~ahB?p1FxPr&mB z1A9_mA6xLmpeu%q^0}UN$ejEbgB)%SHhcan8%Lkud9cQ^&JPLVm}^TX6SBL~v#=Y9 zgV&b(x;D=1jGjykzCY9d{j@K&jZYM4OX%OaKqg8TzI^eR&;s^|2Ip7g_nw~$E-ai+ ze^A+VSR()F(%vtcUYmB^r**a=axe*sV?83xNWqPqrIpWKbx}0N45cf*1@y5g8xNri zx`>^cgKMWUQi{6@2SlnSr-RInt&NB1$km^Ii~%Gr&GtlBYpKvw^Fm>)Tzab(26%52E!g8Tan`K z1P_!(7X)p%Z|vL*Y-YL&W_wI!l^+|7q!-?(6i&5Ml0*%B)zWVOSugTYbEDjIsClfc@XS@03txeU}snN z%rAbg{N_DipI?40EM|10(F!uAL4L`n=f(>beFYkN707DIDK;bRrQeerKjt2>e@iJo z2HKoX9qmu1C&PPC7dRn4>|T+o!@+PLqr-iLR59H_@KhpR2tl?Uv??lhTj5Egz4MVs zZ1QY4yH&uf_Y!DpFrG(lY!uiTbix&@l#!+Gl>DTWX)*uEI^H1Rrl{N6_xJYSzu95e zx-zoqTWfX5krsa|W;Gp`ztq6UH)xqKT^<*U+Y2%&-BDi?rWC(Oo}Je%uN)hb4)<_1 zE2HsOQ`RcDB`HrC96igltj~u^DijK#Y%F%H+b(<{NIEoly`2P6U*UNRs3@Q6&s`%8 zlTME_>uV^vulon!GMktm;rA-18u-B?PrV0h2?xwU`Q)GSVWY0qes{@+E4uy5H&Rlo zBkt~8OMQR2md^GbRg!J`=)9nCDTbU_q!s z$+^KX;3S~5k9Dff3+}I!-Qa;_3OYG98Y{uORf;dO9pY$)RnAf1>}(F}IahXLB$w)M zSS*zE-@aN7dP0a~l-Y$ZZbWw3OVTwAH|FDg;>ybu-;HI6gs_1gy2LBKh~C!@D_?Mb zkdAkdc~jtSF|s9sL{(vD?Kx2nn)MS13s-N?G_!DtDYMgwKMsg)mP%VH2(RGj(IXxR z@ly{?=PuZ)eY&dZu-`psRfKVV>JvC&1c;~@!j_~I_+S`Q+1acE7ls(an=?#8Jp5Iw z6>AA^We|MN_;)uk5Tr=Sf56FJaD_25TJ#+Ug}39&o-4YlQ7j{cyZs!Ch)M!ox#t&CCd7(||xC z5^ahi;>{uhLd}4t=4JqU$H>qC6b>D(i$n)u2@t&*L5-d+HV~rc1$Hxc3w1)@z+Q+Z zp*^B6!BEjSlue+Xql2z}q-`WV6ps!M(2c~05XiQX5WTm~ZI%4C*i29Ntx7l!qIY&X zs_s>{uXUY>B(yHr6l{Vrw*cx|*_Z;sU@I$&)4CSsKua@opcxQk0<^KUw6rxh*ZqU^ zl+j3m7~3n(E`P*Q&LDb!Y$_rm!ZZS8N+boD0c~t-%*-v!EG$fv8Ybi@LU=%=34yHt zrw3;=8AZZ|hGU5Y-EEHnNFpU1qNmLCUt7S3y1Bh`{BNs;$8YcJtu{IQ3i_`!{;f6{ z78Qy%yMiVYDI^qHsjmO0v9fmmInbYqfv9(Sp%hZc+kFP2%+MidJem+rRvsbnZAQw> zZQV&&%rC=dv>w1%E_wgEez0Rhd=KyA#e|FpeGAcqGKQ0VP= z+qT$uwjh`PXlv_4LI;EsNiZTYNE(WE%x8Uce1i^K5OA(19Wma3$*|O&std9K%rJ(3oDB=AgDD^?+@F+ zzt!r`eM7-Okdrmo5(xf(Y|WI(1fp!ivEd=;Kfrl5B>WF91iK9o+mHZ4kg^;mfoMzs zB_v$W;T$@E5{|`CLUeBggoZ}xMiB7<1YImaIStoU0+LL@q%PpbF;cAog+{K*{vzz*f-?b;V_*FISv)JbmOH%6LSA}#k`jhNh$0S>)wZ$RKb zlo~2qV!-!+T|2i)IqurCOZnTod)Kbrd-v_xvv<#)ef#%)q@;cO_aE3M<#_Pmfdi`B zbmSeW?ApD1-`;(P_wPS^A;DmztGwq62qluyN- zZ@+K%vOVnCyKlG3j$J!d-qJgJ9nw>-t*~a zdx2*!2dEu6voB2VQO6T?%D3rA(sz%;;TGqvQLOr%zuwqb4xeI* z30lov1XmG)(uGXsaM&>zOpteGdd8BMd<)QMRh37}+KFwgx*25LX*vw2BRsh!_-Jdk z0S9yH!bDz6j0dN(mMj^IIqB}ar=JSG20GR3(#czE?5>u?*{H*_lxUA}Q?~-qZHV>$i@RT6kuqDfU+hdvwpjop#r{GP7Rh zG6vj<%AbL`RA22tOrNGv>K|FTR?&$~eZ}oTlo~o=6v4d=bki_q8(=#y7|Sj`|F=#c zuPZ_lS_XM+9MMaNwm)r0K~#DD7+zSbDrMv~(7&`8Gn%1ilTLE;3K--dF@@zsOY{?$ z3x{%K!uw2cgIK8Pw5yq4b+q2F(Yc>H z!&<||Cx%K!<*;b$#cnXpvnuxvq^fYdp}Y9%`*aSlFmkhxq$A|Tvb{PN>WT+8Yr|?D z&BoK6yrm~5^5j2lM${*yHm`c7-yd{osTfKjw1AFIFxWYZc??c=a(423Jq$PdbHVip zM5E)Abu;2C!8LyZ*rAYV`?8H)s;b$VXInJRlReFvODi-&PWYvHRCi1LV#4~(T(OB5 z&a@CVu8v@(5?DS_%_!mhD7ahy;wul)5 zF^-k6keR`{%+ri2IF-U-r(ig2Lw0s2r|^-Ue!Xs8?47X2(e-{~W9?`ef$>B-=sdZu z1&1<$n;}=4Hai#QZF48*>`ppmL}#jNouImanXe@|=g|&n)%|7&HQ4$W_sCYo2PIy* zo9(l4j}1l;-dFENr+E<`j{}DclUWd0xXAJ2iwVak;@O1zI-~2i^G?;uwH7XLmLb*m zf{kY3@x!%8hfwue9%>HAuE0tC5V8Mci_I6g)h*;0a_@`?J}I}031*ax^o%vC7GG)Z z>;tnt8HkPfF`ylVt7AXx%f3bXZ3Jke5iBf=uEDtv2w!(x0`iMMCDw{8HK}jyWRzW= zDD}>rU=EHSFMzFG;4miWG*va=!$^a0>lwtiwch=k=w8A{P$qc}1e&@kSb5S;k~i@` z>Pl_nNF`eU+;6QTeV@zV4#`h%C?JWXtY?)yLpphB~& zUtaiWGdE_$qMDxvChlD}rfp>1T#VLiJJt`AS0*l0+pYUwYkER6+sE#ztFDmYLM5j! zi$1xwd)IN* z6&MA#@~aH{pu;$@05Z7xc~71ASqUT>9v>HTMlS*D+;X&tb=nO9k9XCFv-NaJ!`fgn z+}(NHVnRtT0Vy34Ip~~4ooR#BOT8Yt50$so<2T}~XZm{k6b@T}F_YtqT{Qdl+lTx5 zb<-fv`6upr3A0%44^UWJcTZ)3Ox&9E1mY_zT!1@ z6&CPy4#VTFg&kjF^M>l>KfJCY)2|ad$2(=>c)Wl17NEPa1I+ra;zrU6{1;?_|IEpo zB0j#J*2nuQs#9uwlrbBP)5>@mHIYwQPXr-)Z~XB1O7$F*=1Ik81t(`0niJ z24B~>R8ix=UvaL_4k3BL6dI7PzGu|9iUygk#90WAI4^zBQsDHbBpH(n?%HK?z>oBtF|NJGtsvh(cdMyvK ze)VZ^&3UTq>DvDW3%`PS0lT#=N9TNU5!d?9Q&}ZjfI+fjFK+97LzRrwV zBI(|^m}=5$@fg`FVR~RTir%o121tT9|CC#-W-h7u(+I!!K_=?b+(zZFW1;`5E3bHr z=KFjjMVf#f*wFs*ah;V2GV|H`jPmBv$eJ(K|LR3e?;gm#>fIH{z;+GbjLhaJ&zD1Rp;&mOE z&tuKVr!<4uh10xgyrbUwjPZ^7*LKX7?y#7U>#v`ey7_;^!8Vl^dyH`?9Ix0Q2)yqv z)ShzS`dcozxQ@*nY=Gcm{vpi_-U7h+J@;-UB~LA`>XataO)Q;V<1H+^R8PxeUT=Iw z!^~tEcF4xl+k<%~xA0hczvx4{K=)KEW{uS8Awy2itc4+GshnoC7`=!nxH#)71Yc=7Q&Y?%j0ig4R0TgM5j7zzjMHA{ z1k;eH!Qkg-V;WwrwP|rLjx(de)P_0@T5CpJasoQ#q#TEWzM^FM#pBe`2Xo_R6poFi z!{WmJs=SL{+Apd~hlf29$4Bd=$+U-VJw#_{Ik!;i1_TO)19=ZDM`6pt-kHb~RTV^! zr)#-6QXgDP2~$nHq6YGd>+x9dTGFvHmo=w{-un1N+8p_);J8MCc2a#goI+^+vPWC^TsdPL(F5r`1McbJs%#zuT6wR*Sq7@W}!Lqb@ATv(=RZd?@2)AR`!l$ip zUU5!)zu6ozBZ7f#yoJaqc>`t*bEf9&Xa#BO z6&QRmEEe@a8>~!Zi*26xHCelQB$i)t*JIW4<&`FWx_-ApJSsTUO?!f8c;c@QU2ezp zO2r%`n30Q7dSHn9x*Sll&2_=cEr8s6vlutZf~_X)p`xejtxknDj4$2dKJBk+S9OC# z80By}^dISa&z^&ca$86`yk}?BHH;^k#Rj2flO+{C9r!k)!sdPnQL5$sdr;r05UxIb z+PC<~QgkMqip$<8K5mJ7xcZduYEu|WQ&h~HDV4{GQ3C&rF>ii4T@7-_-%_OR95u?! zSwB*qMQu06?8uX+EPLS;=Ny3)TYpH{hS+f;;f>CMDOw`vo87Fv9#<6fX7$iyJ#gZ< zY<${adhI}bbfQJ^6H@p4-~s?RfbDbS-%DJa>^ICw^mp+ z?3g>(KQzAy{{#uJ!TbT}T1bP(#8a^+A}5CwFcw#ay$B(^(P5Y^E+Y8&ranZ=UhXqw;R$ z{~;Uf@ulcFLWHnEA2m>(i5IawCt=rp)_v1K)S#9(;~ahB?p1FxPr&mB z1A9_mA6xLmpeu%q^0}UN$ejEbgB)%SHhcan8%Lkud9cQ^&JPLVm}^TX6SBL~v#=Y9 zgV&b(x;D=1jGjykzCY9d{j@K&jZYM4OX%OaKqg8TzI^eR&;s^|2Ip7g_nw~$E-ai+ ze^A+VSR()F(%vtcUYmB^r**a=axe*sV?83xNWqPqrIpWKbx}0N45cf*1@y5g8xNri zx`>^cgKMWUQi{6@2SlnSr-RInt&NB1$km^Ii~%Gr&GtlBYpKvw^Fm>)Tzab(26%52E!g8Tan`K z1P_!(7X)p%Z|vL*Y-YL&W_wI!l^+|7q!-?(6i&5Ml0*%B)zWVOSugTYbEDjIsClfc@XS@03txeU}snN z%rAbg{N_DipI?40EM|10(F!uAL4L`n=f(>beFYkN707DIDK;bRrQeerKjt2>e@iJo z2HKoX9qmu1C&PPC7dRn4>|T+o!@+PLqr-iLR59H_@KhpR2tl?Uv??lhTj5Egz4MVs zZ1QY4yH&uf_Y!DpFrG(lY!uiTbix&@l#!+Gl>DTWX)*uEI^H1Rrl{N6_xJYSzu95e zx-zoqTWfX5krsa|W;Gp`ztq6UH)xqKT^<*U+Y2%&-BDi?rWC(Oo}Je%uN)hb4)<_1 zE2HsOQ`RcDB`HrC96igltj~u^DijK#Y%F%H+b(<{NIEoly`2P6U*UNRs3@Q6&s`%8 zlTME_>uV^vulon!GMktm;rA-18u-B?PrV0h2?xwU`Q)GSVWY0qes{@+E4uy5H&Rlo zBkt~8OMQR2md^GbRg!J`=)9nCDTbU_q!s z$+^KX;3S~5k9Dff3+}I!-Qa;_3OYG98Y{uORf;dO9pY$)RnAf1>}(F}IahXLB$w)M zSS*zE-@aN7dP0a~l-Y$ZZbWw3OVTwAH|FDg;>ybu-;HI6gs_1gy2LBKh~C!@D_?Mb zkdAkdc~jtSF|s9sL{(vD?Kx2nn)MS13s-N?G_!DtDYMgwKMsg)mP%VH2(RGj(IXxR z@ly{?=PuZ)eY&dZu-`psRfKVV>JvC&1c;~@!j_~I_+S`Q+1acE7ls(an=?#8Jp5Iw z6>AA^We|MN_;)uk5Tr=Sf56FJaD_25TJ#+Ug}39&o-4YlQ7j{cyZs!Ch)M!ox#t&CCd7(||xC z5^ahi;>{uhLd}4t=4JqU$H>qC6b>D(i$n)u2@t&*L5-d+HV~rc1$Hxc3w1)@z+Q+Z zp*^B6!BEjSlue+Xql2z}q-`WV6ps!M(2c~05XiQX5WTm~ZI%4C*i29Ntx7l!qIY&X zs_s>{uXUY>B(yHr6l{Vrw*cx|*_Z;sU@I$&)4CSsKua@opcxQk0<^KUw6rxh*ZqU^ zl+j3m7~3n(E`P*Q&LDb!Y$_rm!ZZS8N+boD0c~t-%*-v!EG$fv8Ybi@LU=%=34yHt zrw3;=8AZZ|hGU5Y-EEHnNFpU1qNmLCUt7S3y1Bh`{BNs;$8YcJtu{IQ3i_`!{;f6{ z78Qy%yMiVYDI^qHsjmO0v9fmmInbYqfv9(Sp%hZc+kFP2%+MidJem+rRvsbnZAQw> zZQV&&%rC=dv>w1%E_wgEez0Rhd=KyA#e|FpeGAcqGKQ0VP= z+qT$uwjh`PXlv_4LI;EsNiZTYNE(WE%x8Uce1i^K5OA(19Wma3$*|O&std9K%rJ(3oDB=AgDD^?+@F+ zzt!r`eM7-Okdrmo5(xf(Y|WI(1fp!ivEd=;Kfrl5B>WF91iK9o+mHZ4kg^;mfoMzs zB_v$W;T$@E5{|`CLUeBggoZ}xMiB7<1YImaIStoU0+LL@q%PpbF;cAog+{K*{vzz*f-?b;V_*FISv)JbmOH%6LSA}#k`jhNh$0S>)wZ$RKb zlo~2qV!-!+T|2i)IqurCOZnTod)Kbrd-v_xvv<#)ef#%)q@;cO_aE3M<#_Pmfdi`B zbmSeW?ApD1-`;(P_wPS^A;DmztGwq62qluyN- zZ@+K%vOVnCyKlG3j$J!d-qJgJ9nw>-t*~a zdx2*!2dEu6voB2VQO6T?%D3rA(sz%;;TGqvQLOr%zuwqb4xeI* z30lov1XmG)(uGXsaM&>zOpteGdd8BMd<)QMRh37}+KFwgx*25LX*vw2BRsh!_-Jdk z0S9yH!bDz6j0dN(mMj^IIqB}ar=JSG20GR3(#czE?5>u?*{H*_lxUA}Q?~-qZHV>$i@RT6kuqDfU+hdvwpjop#r{GP7Rh zG6vj<%AbL`RA22tOrNGv>K|FTR?&$~eZ}oTlo~o=6v4d=bki_q8(=#y7|Sj`|F=#c zuPZ_lS_XM+9MMaNwm)r0K~#DD7+zSbDrMv~(7&`8Gn%1ilTLE;3K--dF@@zsOY{?$ z3x{%K!uw2cgIK8Pw5yq4b+q2F(Yc>H z!&<||Cx%K!<*;b$#cnXpvnuxvq^fYdp}Y9%`*aSlFmkhxq$A|Tvb{PN>WT+8Yr|?D z&BoK6yrm~5^5j2lM${*yHm`c7-yd{osTfKjw1AFIFxWYZc??c=a(423Jq$PdbHVip zM5E)Abu;2C!8LyZ*rAYV`?8H)s;b$VXInJRlReFvODi-&PWYvHRCi1LV#4~(T(OB5 z&a@CVu8v@(5?DS_%_!mhD7ahy;wul)5 zF^-k6keR`{%+ri2IF-U-r(ig2Lw0s2r|^-Ue!Xs8?47X2(e-{~W9?`ef$>B-=sdZu z1&1<$n;}=4Hai#QZF48*>`ppmL}#jNouImanXe@|=g|&n)%|7&HQ4$W_sCYo2PIy* zo9(l4j}1l;-dFENr+E<`j{}DclUWd0xXAJ2iwVak;@O1zI-~2i^G?;uwH7XLmLb*m zf{kY3@x!%8hfwue9%>HAuE0tC5V8Mci_I6g)h*;0a_@`?J}I}031*ax^o%vC7GG)Z z>;tnt8HkPfF`ylVt7AXx%f3bXZ3Jke5iBf=uEDtv2w!(x0`iMMCDw{8HK}jyWRzW= zDD}>rU=EHSFMzFG;4miWG*va=!$^a0>lwtiwch=k=w8A{P$qc}1e&@kSb5S;k~i@` z>Pl_nNF`eU+;6QTeV@zV4#`h%C?JWXtY?)yLpphB~& zUtaiWGdE_$qMDxvChlD}rfp>1T#VLiJJt`AS0*l0+pYUwYkER6+sE#ztFDmYLM5j! zi$1xwd)IN* z6&MA#@~aH{pu;$@05Z7xc~71ASqUT>9v>HTMlS*D+;X&tb=nO9k9XCFv-NaJ!`fgn z+}(NHVnRtT0Vy34Ip~~4ooR#BOT8Yt50$so<2T}~XZm{k6b@T}F_YtqT{Qdl+lTx5 zb<-fv`6upr3A0%44^UWJcTZ)3Ox&9E1mY_zT!1@ z6&CPy4#VTFg&kjF^M>l>KfJCY)2|ad$2(=>c)Wl17NEPa1I+ra;zrU6{1;?_|IEpo zB0j#J*2nuQs#9uwlrbBP)5>@mHIYwQPXr-)Z~XB1O7$F*=1Ik81t(`0niJ z24B~>R8ix=UvaL_4k3BL6dI7PzGu|9iUygk#90WAI4^zBQsDHbBpH(n?%HK?z>oBtF|NJGtsvh(cdMyvK ze)VZ^&3UTq>DvDW3%`PS0lT#=N9TNU5!d?9Q&}ZjfI+fjFK+97LzRrwV zBI(|^m}=5$@fg`FVR~RTir%o121tT9|CC#-W-h7u(+I!!K_=?b+(zZFW1;`5E3bHr z=KFjjMVf#f*wFs*ah;V2GV|H`jPmBv$eJ(K|LR3e?;gm#>fIH{z;+GbjLhaJ&zD1Rp;&mOE z&tuKVr!<4uh10xgyrbUwjPZ^7*LKX7?y#7U>#v`ey7_;^!8Vl^dyH`?9Ix0Q2)yqv z)ShzS`dcozxQ@*nY=Gcm{vpi_-U7h+J@;-UB~LA`>XataO)Q;V<1H+^R8PxeUT=Iw z!^~tEcF4xl+k<%~xA0hczvx4{K=)KEW{uS8Awy2itc4+GshnoC7`=!nxH#)71Yc=7Q&Y?%j0ig4R0TgM5j7zzjMHA{ z1k;eH!Qkg-V;WwrwP|rLjx(de)P_0@T5CpJasoQ#q#TEWzM^FM#pBe`2Xo_R6poFi z!{WmJs=SL{+Apd~hlf29$4Bd=$+U-VJw#_{Ik!;i1_TO)19=ZDM`6pt-kHb~RTV^! zr)#-6QXgDP2~$nHq6YGd>+x9dTGFvHmo=w{-un1N+8p_);J8MCc2a#goI+^+vPWC^TsdPL(F5r`1McbJs%#zuT6wR*Sq7@W}!Lqb@ATv(=RZd?@2)AR`!l$ip zUU5!)zu6ozBZ7f#yoJaqc>`t*bEf9&Xa#BO z6&QRmEEe@a8>~!Zi*26xHCelQB$i)t*JIW4<&`FWx_-ApJSsTUO?!f8c;c@QU2ezp zO2r%`n30Q7dSHn9x*Sll&2_=cEr8s6vlutZf~_X)p`xejtxknDj4$2dKJBk+S9OC# z80By}^dISa&z^&ca$86`yk}?BHH;^k#Rj2flO+{C9r!k)!sdPnQL5$sdr;r05UxIb z+PC<~QgkMqip$<8K5mJ7xcZduYEu|WQ&h~HDV4{GQ3C&rF>ii4T@7-_-%_OR95u?! zSwB*qMQu06?8uX+EPLS;=Ny3)TYpH{hS+f;;f>CMDOw`vo87Fv9#<6fX7$iyJ#gZ< zY<${adhI}bbfQJ^6H@p4-~s?RfbDbS-%DJa>^ICw^mp+ z?3g>(KQzAy{{#uJ!TbT}T1bP(#8a^+A}5CwFcw#ay$B(^(P5Y^E+Y8&ranZ=UhXqw;R$ z{~;Uf@ulcFLWHnEA2m>(i5IawCt=rp)_v1K)S#9(;~ahB?p1FxPr&mB z1A9_mA6xLmpeu%q^0}UN$ejEbgB)%SHhcan8%Lkud9cQ^&JPLVm}^TX6SBL~v#=Y9 zgV&b(x;D=1jGjykzCY9d{j@K&jZYM4OX%OaKqg8TzI^eR&;s^|2Ip7g_nw~$E-ai+ ze^A+VSR()F(%vtcUYmB^r**a=axe*sV?83xNWqPqrIpWKbx}0N45cf*1@y5g8xNri zx`>^cgKMWUQi{6@2SlnSr-RInt&NB1$km^Ii~%Gr&GtlBYpKvw^Fm>)Tzab(26%52E!g8Tan`K z1P_!(7X)p%Z|vL*Y-YL&W_wI!l^+|7q!-?(6i&5Ml0*%B)zWVOSugTYbEDjIsClfc@XS@03txeU}snN z%rAbg{N_DipI?40EM|10(F!uAL4L`n=f(>beFYkN707DIDK;bRrQeerKjt2>e@iJo z2HKoX9qmu1C&PPC7dRn4>|T+o!@+PLqr-iLR59H_@KhpR2tl?Uv??lhTj5Egz4MVs zZ1QY4yH&uf_Y!DpFrG(lY!uiTbix&@l#!+Gl>DTWX)*uEI^H1Rrl{N6_xJYSzu95e zx-zoqTWfX5krsa|W;Gp`ztq6UH)xqKT^<*U+Y2%&-BDi?rWC(Oo}Je%uN)hb4)<_1 zE2HsOQ`RcDB`HrC96igltj~u^DijK#Y%F%H+b(<{NIEoly`2P6U*UNRs3@Q6&s`%8 zlTME_>uV^vulon!GMktm;rA-18u-B?PrV0h2?xwU`Q)GSVWY0qes{@+E4uy5H&Rlo zBkt~8OMQR2md^GbRg!J`=)9nCDTbU_q!s z$+^KX;3S~5k9Dff3+}I!-Qa;_3OYG98Y{uORf;dO9pY$)RnAf1>}(F}IahXLB$w)M zSS*zE-@aN7dP0a~l-Y$ZZbWw3OVTwAH|FDg;>ybu-;HI6gs_1gy2LBKh~C!@D_?Mb zkdAkdc~jtSF|s9sL{(vD?Kx2nn)MS13s-N?G_!DtDYMgwKMsg)mP%VH2(RGj(IXxR z@ly{?=PuZ)eY&dZu-`psRfKVV>JvC&1c;~@!j_~I_+S`Q+1acE7ls(an=?#8Jp5Iw z6>AA^We|MN_;)uk5Tr=Sf56FJaD_25TJ#+Ug}39&o-4YlQ7j{cyZs!Ch)M!ox#t&CCd7(||xC z5^ahi;>{uhLd}4t=4JqU$H>qC6b>D(i$n)u2@t&*L5-d+HV~rc1$Hxc3w1)@z+Q+Z zp*^B6!BEjSlue+Xql2z}q-`WV6ps!M(2c~05XiQX5WTm~ZI%4C*i29Ntx7l!qIY&X zs_s>{uXUY>B(yHr6l{Vrw*cx|*_Z;sU@I$&)4CSsKua@opcxQk0<^KUw6rxh*ZqU^ zl+j3m7~3n(E`P*Q&LDb!Y$_rm!ZZS8N+boD0c~t-%*-v!EG$fv8Ybi@LU=%=34yHt zrw3;=8AZZ|hGU5Y-EEHnNFpU1qNmLCUt7S3y1Bh`{BNs;$8YcJtu{IQ3i_`!{;f6{ z78Qy%yMiVYDI^qHsjmO0v9fmmInbYqfv9(Sp%hZc+kFP2%+MidJem+rRvsbnZAQw> zZQV&&%rC=dv>w1%E_wgEez0Rhd=KyA#e|FpeGAcqGKQ0VP= z+qT$uwjh`PXlv_4LI;EsNiZTYNE(WE%x8Uce1i^K5OA(19Wma3$*|O&std9K%rJ(3oDB=AgDD^?+@F+ zzt!r`eM7-Okdrmo5(xf(Y|WI(1fp!ivEd=;Kfrl5B>WF91iK9o+mHZ4kg^;mfoMzs zB_v$W;T$@E5{|`CLUeBggoZ}xMiB7<1YImaIStoU0+LL@q%PpbF;cAog+{K*{vzz*f-?b;V_*FISv)JbmOH%6LSA}#k`jhNh$0S>)wZ$RKb zlo~2qV!-!+T|2i)IqurCOZnTod)Kbrd-v_xvv<#)ef#%)q@;cO_aE3M<#_Pmfdi`B zbmSeW?ApD1-`;(P_wPS^A;DmztGwq62qluyN- zZ@+K%vOVnCyKlG3j$J!d-qJgJ9nw>-t*~a zdx2*!2dEu6voB2VQO6T?%D3rA(sz%;;TGqvQLOr%zuwqb4xeI* z30lov1XmG)(uGXsaM&>zOpteGdd8BMd<)QMRh37}+KFwgx*25LX*vw2BRsh!_-Jdk z0S9yH!bDz6j0dN(mMj^IIqB}ar=JSG20GR3(#czE?5>u?*{H*_lxUA}Q?~-qZHV>$i@RT6kuqDfU+hdvwpjop#r{GP7Rh zG6vj<%AbL`RA22tOrNGv>K|FTR?&$~eZ}oTlo~o=6v4d=bki_q8(=#y7|Sj`|F=#c zuPZ_lS_XM+9MMaNwm)r0K~#DD7+zSbDrMv~(7&`8Gn%1ilTLE;3K--dF@@zsOY{?$ z3x{%K!uw2cgIK8Pw5yq4b+q2F(Yc>H z!&<||Cx%K!<*;b$#cnXpvnuxvq^fYdp}Y9%`*aSlFmkhxq$A|Tvb{PN>WT+8Yr|?D z&BoK6yrm~5^5j2lM${*yHm`c7-yd{osTfKjw1AFIFxWYZc??c=a(423Jq$PdbHVip zM5E)Abu;2C!8LyZ*rAYV`?8H)s;b$VXInJRlReFvODi-&PWYvHRCi1LV#4~(T(OB5 z&a@CVu8v@(5?DS_%_!mhD7ahy;wul)5 zF^-k6keR`{%+ri2IF-U-r(ig2Lw0s2r|^-Ue!Xs8?47X2(e-{~W9?`ef$>B-=sdZu z1&1<$n;}=4Hai#QZF48*>`ppmL}#jNouImanXe@|=g|&n)%|7&HQ4$W_sCYo2PIy* zo9(l4j}1l;-dFENr+E<`j{}DclUWd0xXAJ2iwVak;@O1zI-~2i^G?;uwH7XLmLb*m zf{kY3@x!%8hfwue9%>HAuE0tC5V8Mci_I6g)h*;0a_@`?J}I}031*ax^o%vC7GG)Z z>;tnt8HkPfF`ylVt7AXx%f3bXZ3Jke5iBf=uEDtv2w!(x0`iMMCDw{8HK}jyWRzW= zDD}>rU=EHSFMzFG;4miWG*va=!$^a0>lwtiwch=k=w8A{P$qc}1e&@kSb5S;k~i@` z>Pl_nNF`eU+;6QTeV@zV4#`h%C?JWXtY?)yLpphB~& zUtaiWGdE_$qMDxvChlD}rfp>1T#VLiJJt`AS0*l0+pYUwYkER6+sE#ztFDmYLM5j! zi$1xwd)IN* z6&MA#@~aH{pu;$@05Z7xc~71ASqUT>9v>HTMlS*D+;X&tb=nO9k9XCFv-NaJ!`fgn z+}(NHVnRtT0Vy34Ip~~4ooR#BOT8Yt50$so<2T}~XZm{k6b@T}F_YtqT{Qdl+lTx5 zb<-fv`6upr3A0%44^UWJcTZ)3Ox&9E1mY_zT!1@ z6&CPy4#VTFg&kjF^M>l>KfJCY)2|ad$2(=>c)Wl17NEPa1I+ra;zrU6{1;?_|IEpo zB0j#J*2nuQs#9uwlrbBP)5>@mHIYwQPXr-)Z~XB1O7$F*=1Ik81t(`0niJ z24B~>R8ix=UvaL_4k3BL6dI7PzGu|9iUygk#90WAI4^zBQsDHbBpH(n?%HK?z>oBtF|NJGtsvh(cdMyvK ze)VZ^&3UTq>DvDW3%`PS0lT#=N9TNU5!d?9Q&}ZjfI+fjFK+97LzRrwV zBI(|^m}=5$@fg`FVR~RTir%o121tT9|CC#-W-h7u(+I!!K_=?b+(zZFW1;`5E3bHr z=KFjjMVf#f*wFs*ah;V2GV|H`jPmBv$eJ(K|LR3e?;gm#>fIH{z;+GbjLhaJ&zD1Rp;&mOE z&tuKVr!<4uh10xgyrbUwjPZ^7*LKX7?y#7U>#v`ey7_;^!8Vl^dyH`?9Ix0Q2)yqv z)ShzS`dcozxQ@*nY=Gcm{vpi_-U7h+J@;-UB~LA`>XataO)Q;V<1H+^R8PxeUT=Iw z!^~tEcF4xl+k<%~xA0hczvx4{K=)KEW{uS8Awy2itc4+GshnoC7`=!nxH#)71Yc=7Q&Y?%j0ig4R0TgM5j7zzjMHA{ z1k;eH!Qkg-V;WwrwP|rLjx(de)P_0@T5CpJasoQ#q#TEWzM^FM#pBe`2Xo_R6poFi z!{WmJs=SL{+Apd~hlf29$4Bd=$+U-VJw#_{Ik!;i1_TO)19=ZDM`6pt-kHb~RTV^! zr)#-6QXgDP2~$nHq6YGd>+x9dTGFvHmo=w{-un1N+8p_);J8MCc2a#goI+^+vPWC^TsdPL(F5r`1McbJs%#zuT6wR*Sq7@W}!Lqb@ATv(=RZd?@2)AR`!l$ip zUU5!)zu6ozBZ7f#yoJaqc>`t*bEf9&Xa#BO z6&QRmEEe@a8>~!Zi*26xHCelQB$i)t*JIW4<&`FWx_-ApJSsTUO?!f8c;c@QU2ezp zO2r%`n30Q7dSHn9x*Sll&2_=cEr8s6vlutZf~_X)p`xejtxknDj4$2dKJBk+S9OC# z80By}^dISa&z^&ca$86`yk}?BHH;^k#Rj2flO+{C9r!k)!sdPnQL5$sdr;r05UxIb z+PC<~QgkMqip$<8K5mJ7xcZduYEu|WQ&h~HDV4{GQ3C&rF>ii4T@7-_-%_OR95u?! zSwB*qMQu06?8uX+EPLS;=Ny3)TYpH{hS+f;;f>CMDOw`vo87Fv9#<6fX7$iyJ#gZ< zY<${adhI}bbfQJ^6H@p4-~s?RfbDbS-%DJa>^ICw^mp+ z?3g>(KQzAy{{#uJ!TbT}T1bP(#8a^+A}5CwFcw#ay$B(^(P5Y^E+Y8&ranZ=UhXqw;R$ z{~;Uf@ulcFLWHnEA2m>(i5IawCt=rp)_v1K)S#9(;~ahB?p1FxPr&mB z1A9_mA6xLmpeu%q^0}UN$ejEbgB)%SHhcan8%Lkud9cQ^&JPLVm}^TX6SBL~v#=Y9 zgV&b(x;D=1jGjykzCY9d{j@K&jZYM4OX%OaKqg8TzI^eR&;s^|2Ip7g_nw~$E-ai+ ze^A+VSR()F(%vtcUYmB^r**a=axe*sV?83xNWqPqrIpWKbx}0N45cf*1@y5g8xNri zx`>^cgKMWUQi{6@2SlnSr-RInt&NB1$km^Ii~%Gr&GtlBYpKvw^Fm>)Tzab(26%52E!g8Tan`K z1P_!(7X)p%Z|vL*Y-YL&W_wI!l^+|7q!-?(6i&5Ml0*%B)zWVOSugTYbEDjIsClfc@XS@03txeU}snN z%rAbg{N_DipI?40EM|10(F!uAL4L`n=f(>beFYkN707DIDK;bRrQeerKjt2>e@iJo z2HKoX9qmu1C&PPC7dRn4>|T+o!@+PLqr-iLR59H_@KhpR2tl?Uv??lhTj5Egz4MVs zZ1QY4yH&uf_Y!DpFrG(lY!uiTbix&@l#!+Gl>DTWX)*uEI^H1Rrl{N6_xJYSzu95e zx-zoqTWfX5krsa|W;Gp`ztq6UH)xqKT^<*U+Y2%&-BDi?rWC(Oo}Je%uN)hb4)<_1 zE2HsOQ`RcDB`HrC96igltj~u^DijK#Y%F%H+b(<{NIEoly`2P6U*UNRs3@Q6&s`%8 zlTME_>uV^vulon!GMktm;rA-18u-B?PrV0h2?xwU`Q)GSVWY0qes{@+E4uy5H&Rlo zBkt~8OMQR2md^GbRg!J`=)9nCDTbU_q!s z$+^KX;3S~5k9Dff3+}I!-Qa;_3OYG98Y{uORf;dO9pY$)RnAf1>}(F}IahXLB$w)M zSS*zE-@aN7dP0a~l-Y$ZZbWw3OVTwAH|FDg;>ybu-;HI6gs_1gy2LBKh~C!@D_?Mb zkdAkdc~jtSF|s9sL{(vD?Kx2nn)MS13s-N?G_!DtDYMgwKMsg)mP%VH2(RGj(IXxR z@ly{?=PuZ)eY&dZu-`psRfKVV>JvC&1c;~@!j_~I_+S`Q+1acE7ls(an=?#8Jp5Iw z6>AA^We|MN_;)uk5Tr=Sf56FJaD_25TJ#+Ug}39&o-4YlQ7j{cyZs!Ch)M!ox#t&CCd7(||xC z5^ahi;>{uhLd}4t=4JqU$H>qC6b>D(i$n)u2@t&*L5-d+HV~rc1$Hxc3w1)@z+Q+Z zp*^B6!BEjSlue+Xql2z}q-`WV6ps!M(2c~05XiQX5WTm~ZI%4C*i29Ntx7l!qIY&X zs_s>{uXUY>B(yHr6l{Vrw*cx|*_Z;sU@I$&)4CSsKua@opcxQk0<^KUw6rxh*ZqU^ zl+j3m7~3n(E`P*Q&LDb!Y$_rm!ZZS8N+boD0c~t-%*-v!EG$fv8Ybi@LU=%=34yHt zrw3;=8AZZ|hGU5Y-EEHnNFpU1qNmLCUt7S3y1Bh`{BNs;$8YcJtu{IQ3i_`!{;f6{ z78Qy%yMiVYDI^qHsjmO0v9fmmInbYqfv9(Sp%hZc+kFP2%+MidJem+rRvsbnZAQw> zZQV&&%rC=dv>w1%E_wgEez0Rhd=KyA#e|FpeGAcqGKQ0VP= z+qT$uwjh`PXlv_4LI;EsNiZTYNE(WE%x8Uce1i^K5OA(19Wma3$*|O&std9K%rJ(3oDB=AgDD^?+@F+ zzt!r`eM7-Okdrmo5(xf(Y|WI(1fp!ivEd=;Kfrl5B>WF91iK9o+mHZ4kg^;mfoMzs zB_v$W;T$@E5{|`CLUeBggoZ}xMiB7<1YImaIStoU0+LL@q%PpbF;cAog+{K*{vzz*f-?b;V_*FISv)JbmOH%6LSA}#k`jhNh$0S>)wZ$RKb zlo~2qV!-!+T|2i)IqurCOZnTod)Kbrd-v_xvv<#)ef#%)q@;cO_aE3M<#_Pmfdi`B zbmSeW?ApD1-`;(P_wPS^A;DmztGwq62qluyN- zZ@+K%vOVnCyKlG3j$J!d-qJgJ9nw>-t*~a zdx2*!2dEu6voB2VQO6T?%D3rA(sz%;;TGqvQLOr%zuwqb4xeI* z30lov1XmG)(uGXsaM&>zOpteGdd8BMd<)QMRh37}+KFwgx*25LX*vw2BRsh!_-Jdk z0S9yH!bDz6j0dN(mMj^IIqB}ar=JSG20GR3(#czE?5>u?*{H*_lxUA}Q?~-qZHV>$i@RT6kuqDfU+hdvwpjop#r{GP7Rh zG6vj<%AbL`RA22tOrNGv>K|FTR?&$~eZ}oTlo~o=6v4d=bki_q8(=#y7|Sj`|F=#c zuPZ_lS_XM+9MMaNwm)r0K~#DD7+zSbDrMv~(7&`8Gn%1ilTLE;3K--dF@@zsOY{?$ z3x{%K!uw2cgIK8Pw5yq4b+q2F(Yc>H z!&<||Cx%K!<*;b$#cnXpvnuxvq^fYdp}Y9%`*aSlFmkhxq$A|Tvb{PN>WT+8Yr|?D z&BoK6yrm~5^5j2lM${*yHm`c7-yd{osTfKjw1AFIFxWYZc??c=a(423Jq$PdbHVip zM5E)Abu;2C!8LyZ*rAYV`?8H)s;b$VXInJRlReFvODi-&PWYvHRCi1LV#4~(T(OB5 z&a@CVu8v@(5?DS_%_!mhD7ahy;wul)5 zF^-k6keR`{%+ri2IF-U-r(ig2Lw0s2r|^-Ue!Xs8?47X2(e-{~W9?`ef$>B-=sdZu z1&1<$n;}=4Hai#QZF48*>`ppmL}#jNouImanXe@|=g|&n)%|7&HQ4$W_sCYo2PIy* zo9(l4j}1l;-dFENr+E<`j{}DclUWd0xXAJ2iwVak;@O1zI-~2i^G?;uwH7XLmLb*m zf{kY3@x!%8hfwue9%>HAuE0tC5V8Mci_I6g)h*;0a_@`?J}I}031*ax^o%vC7GG)Z z>;tnt8HkPfF`ylVt7AXx%f3bXZ3Jke5iBf=uEDtv2w!(x0`iMMCDw{8HK}jyWRzW= zDD}>rU=EHSFMzFG;4miWG*va=!$^a0>lwtiwch=k=w8A{P$qc}1e&@kSb5S;k~i@` z>Pl_nNF`eU+;6QTeV@zV4#`h%C?JWXtY?)yLpphB~& zUtaiWGdE_$qMDxvChlD}rfp>1T#VLiJJt`AS0*l0+pYUwYkER6+sE#ztFDmYLM5j! zi$1xwd)IN* z6&MA#@~aH{pu;$@05Z7xc~71ASqUT>9v>HTMlS*D+;X&tb=nO9k9XCFv-NaJ!`fgn z+}(NHVnRtT0Vy34Ip~~4ooR#BOT8Yt50$so<2T}~XZm{k6b@T}F_YtqT{Qdl+lTx5 zb<-fv`6upr3A0%44^UWJcTZ)3Ox&9E1mY_zT!1@ z6&CPy4#VTFg&kjF^M>l>KfJCY)2|ad$2(=>c)Wl17NEPa1I+ra;zrU6{1;?_|IEpo zB0j#J*2nuQs#9uwlrbBP)5>@mHIYwQPXr-)Z~XB1O7$F*=1Ik81t(`0niJ z24B~>R8ix=UvaL_4k3BL6dI7PzGu|9iUygk#90WAI4^zBQsDHbBpH(n?%HK?z>oBtF|NJGtsvh(cdMyvK ze)VZ^&3UTq>DvDW3%`PS0lT#=N9TNU5!d?9Q&}ZjfI+fjFK+97LzRrwV zBI(|^m}=5$@fg`FVR~RTir%o121tT9|CC#-W-h7u(+I!!K_=?b+(zZFW1;`5E3bHr z=KFjjMVf#f*wFs*ah;V2GV|H`jPmBv$eJ(K|LR3e?;gm#>fIH{z;+GbjLhaJ&zD1Rp;&mOE z&tuKVr!<4uh10xgyrbUwjPZ^7*LKX7?y#7U>#v`ey7_;^!8Vl^dyH`?9Ix0Q2)yqv z)ShzS`dcozxQ@*nY=Gcm{vpi_-U7h+J@;-UB~LA`>XataO)Q;V<1H+^R8PxeUT=Iw z!^~tEcF4xl+k<%~xA0hczvx4{K=)KEW{uS8Awy2itc4+GshnoC7`=!nxH#)71Yc=7Q&Y?%j0ig4R0TgM5j7zzjMHA{ z1k;eH!Qkg-V;WwrwP|rLjx(de)P_0@T5CpJasoQ#q#TEWzM^FM#pBe`2Xo_R6poFi z!{WmJs=SL{+Apd~hlf29$4Bd=$+U-VJw#_{Ik!;i1_TO)19=ZDM`6pt-kHb~RTV^! zr)#-6QXgDP2~$nHq6YGd>+x9dTGFvHmo=w{-un1N+8p_);J8MCc2a#goI+^+vPWC^TsdPL(F5r`1McbJs%#zuT6wR*Sq7@W}!Lqb@ATv(=RZd?@2)AR`!l$ip zUU5!)zu6ozBZ7f#yoJaqc>`t*bEf9&Xa#BO z6&QRmEEe@a8>~!Zi*26xHCelQB$i)t*JIW4<&`FWx_-ApJSsTUO?!f8c;c@QU2ezp zO2r%`n30Q7dSHn9x*Sll&2_=cEr8s6vlutZf~_X)p`xejtxknDj4$2dKJBk+S9OC# z80By}^dISa&z^&ca$86`yk}?BHH;^k#Rj2flO+{C9r!k)!sdPnQL5$sdr;r05UxIb z+PC<~QgkMqip$<8K5mJ7xcZduYEu|WQ&h~HDV4{GQ3C&rF>ii4T@7-_-%_OR95u?! zSwB*qMQu06?8uX+EPLS;=Ny3)TYpH{hS+f;;f>CMDOw`vo87Fv9#<6fX7$iyJ#gZ< zY<${adhI}bbfQJ^6H@p4-~s?RfbDbS-%DJa>^ICw^mp+ z?3g>(KQzAy{{#uJ!TbT}T1bP(#8a^+A}5CwFcw#ay$B(^(P5Y^E+Y8&ranZ=UhXqw;R$ z{~;Uf@ulcFLWHnEA2m>(i5IawCt=rp)_v1K)S#9(;~ahB?p1FxPr&mB z1A9_mA6xLmpeu%q^0}UN$ejEbgB)%SHhcan8%Lkud9cQ^&JPLVm}^TX6SBL~v#=Y9 zgV&b(x;D=1jGjykzCY9d{j@K&jZYM4OX%OaKqg8TzI^eR&;s^|2Ip7g_nw~$E-ai+ ze^A+VSR()F(%vtcUYmB^r**a=axe*sV?83xNWqPqrIpWKbx}0N45cf*1@y5g8xNri zx`>^cgKMWUQi{6@2SlnSr-RInt&NB1$km^Ii~%Gr&GtlBYpKvw^Fm>)Tzab(26%52E!g8Tan`K z1P_!(7X)p%Z|vL*Y-YL&W_wI!l^+|7q!-?(6i&5Ml0*%B)zWVOSugTYbEDjIsClfc@XS@03txeU}snN z%rAbg{N_DipI?40EM|10(F!uAL4L`n=f(>beFYkN707DIDK;bRrQeerKjt2>e@iJo z2HKoX9qmu1C&PPC7dRn4>|T+o!@+PLqr-iLR59H_@KhpR2tl?Uv??lhTj5Egz4MVs zZ1QY4yH&uf_Y!DpFrG(lY!uiTbix&@l#!+Gl>DTWX)*uEI^H1Rrl{N6_xJYSzu95e zx-zoqTWfX5krsa|W;Gp`ztq6UH)xqKT^<*U+Y2%&-BDi?rWC(Oo}Je%uN)hb4)<_1 zE2HsOQ`RcDB`HrC96igltj~u^DijK#Y%F%H+b(<{NIEoly`2P6U*UNRs3@Q6&s`%8 zlTME_>uV^vulon!GMktm;rA-18u-B?PrV0h2?xwU`Q)GSVWY0qes{@+E4uy5H&Rlo zBkt~8OMQR2md^GbRg!J`=)9nCDTbU_q!s z$+^KX;3S~5k9Dff3+}I!-Qa;_3OYG98Y{uORf;dO9pY$)RnAf1>}(F}IahXLB$w)M zSS*zE-@aN7dP0a~l-Y$ZZbWw3OVTwAH|FDg;>ybu-;HI6gs_1gy2LBKh~C!@D_?Mb zkdAkdc~jtSF|s9sL{(vD?Kx2nn)MS13s-N?G_!DtDYMgwKMsg)mP%VH2(RGj(IXxR z@ly{?=PuZ)eY&dZu-`psRfKVV>JvC&1c;~@!j_~I_+S`Q+1acE7ls(an=?#8Jp5Iw z6>AA^We|MN_;)uk5Tr=Sf56FJaD_25TJ#+Ug}39&o-4YlQ7j{cyZs!Ch)M!ox#t&CCd7(||xC z5^ahi;>{uhLd}4t=4JqU$H>qC6b>D(i$n)u2@t&*L5-d+HV~rc1$Hxc3w1)@z+Q+Z zp*^B6!BEjSlue+Xql2z}q-`WV6ps!M(2c~05XiQX5WTm~ZI%4C*i29Ntx7l!qIY&X zs_s>{uXUY>B(yHr6l{Vrw*cx|*_Z;sU@I$&)4CSsKua@opcxQk0<^KUw6rxh*ZqU^ zl+j3m7~3n(E`P*Q&LDb!Y$_rm!ZZS8N+boD0c~t-%*-v!EG$fv8Ybi@LU=%=34yHt zrw3;=8AZZ|hGU5Y-EEHnNFpU1qNmLCUt7S3y1Bh`{BNs;$8YcJtu{IQ3i_`!{;f6{ z78Qy%yMiVYDI^qHsjmO0v9fmmInbYqfv9(Sp%hZc+kFP2%+MidJem+rRvsbnZAQw> zZQV&&%rC=dv>w1%E_wgEez0Rhd=KyA#e|FpeGAcqGKQ0VP= z+qT$uwjh`PXlv_4LI;EsNiZTYNE(WE%x8Uce1i^K5OA(19Wma3$*|O&std9K%rJ(3oDB=AgDD^?+@F+ zzt!r`eM7-Okdrmo5(xf(Y|WI(1fp!ivEd=;Kfrl5B>WF91iK9o+mHZ4kg^;mfoMzs zB_v$W;T$@E5{|`CLUeBggoZ}xMiB7<1YImaIStoU0+LL@q%PpbF;cAog+{K*{vzz*f-?b;V_*FISv)JbmOH%6LSA}#k`jhNh$0S>)wZ$RKb zlo~2qV!-!+T|2i)IqurCOZnTod)Kbrd-v_xvv<#)ef#%)q@;cO_aE3M<#_Pmfdi`B zbmSeW?ApD1-`;(P_wPS^A;DmztGwq62qluyN- zZ@+K%vOVnCyKlG3j$J!d-qJgJ9nw>-t*~a zdx2*!2dEu6voB2VQO6T?%D3rA(sz%;;TGqvQLOr%zuwqb4xeI* z30lov1XmG)(uGXsaM&>zOpteGdd8BMd<)QMRh37}+KFwgx*25LX*vw2BRsh!_-Jdk z0S9yH!bDz6j0dN(mMj^IIqB}ar=JSG20GR3(#czE?5>u?*{H*_lxUA}Q?~-qZHV>$i@RT6kuqDfU+hdvwpjop#r{GP7Rh zG6vj<%AbL`RA22tOrNGv>K|FTR?&$~eZ}oTlo~o=6v4d=bki_q8(=#y7|Sj`|F=#c zuPZ_lS_XM+9MMaNwm)r0K~#DD7+zSbDrMv~(7&`8Gn%1ilTLE;3K--dF@@zsOY{?$ z3x{%K!uw2cgIK8Pw5yq4b+q2F(Yc>H z!&<||Cx%K!<*;b$#cnXpvnuxvq^fYdp}Y9%`*aSlFmkhxq$A|Tvb{PN>WT+8Yr|?D z&BoK6yrm~5^5j2lM${*yHm`c7-yd{osTfKjw1AFIFxWYZc??c=a(423Jq$PdbHVip zM5E)Abu;2C!8LyZ*rAYV`?8H)s;b$VXInJRlReFvODi-&PWYvHRCi1LV#4~(T(OB5 z&a@CVu8v@(5?DS_%_!mhD7ahy;wul)5 zF^-k6keR`{%+ri2IF-U-r(ig2Lw0s2r|^-Ue!Xs8?47X2(e-{~W9?`ef$>B-=sdZu z1&1<$n;}=4Hai#QZF48*>`ppmL}#jNouImanXe@|=g|&n)%|7&HQ4$W_sCYo2PIy* zo9(l4j}1l;-dFENr+E<`j{}DclUWd0xXAJ2iwVak;@O1zI-~2i^G?;uwH7XLmLb*m zf{kY3@x!%8hfwue9%>HAuE0tC5V8Mci_I6g)h*;0a_@`?J}I}031*ax^o%vC7GG)Z z>;tnt8HkPfF`ylVt7AXx%f3bXZ3Jke5iBf=uEDtv2w!(x0`iMMCDw{8HK}jyWRzW= zDD}>rU=EHSFMzFG;4miWG*va=!$^a0>lwtiwch=k=w8A{P$qc}1e&@kSb5S;k~i@` z>Pl_nNF`eU+;6QTeV@zV4#`h%C?JWXtY?)yLpphB~& zUtaiWGdE_$qMDxvChlD}rfp>1T#VLiJJt`AS0*l0+pYUwYkER6+sE#ztFDmYLM5j! zi$1xwd)IN* z6&MA#@~aH{pu;$@05Z7xc~71ASqUT>9v>HTMlS*D+;X&tb=nO9k9XCFv-NaJ!`fgn z+}(NHVnRtT0Vy34Ip~~4ooR#BOT8Yt50$so<2T}~XZm{k6b@T}F_YtqT{Qdl+lTx5 zb<-fv`6upr3A0%44^UWJcTZ)3Ox&9E1mY_zT!1@ z6&CPy4#VTFg&kjF^M>l>KfJCY)2|ad$2(=>c)Wl17NEPa1I+ra;zrU6{1;?_|IEpo zB0j#J*2nuQs#9uwlrbBP)5>@mHIYwQPXr-)Z~XB1O7$F*=1Ik81t(`0niJ z24B~>R8ix=UvaL_4k3BL6dI7PzGu|9iUygk#90WAI4^zBQsDHbBpH(n?%HK?z>oBtF|NJGtsvh(cdMyvK ze)VZ^&3UTq>DvDW3%`PS0lT#=N9TNU5!d?9Q&}ZjfI+fjFK+97LzRrwV zBI(|^m}=5$@fg`FVR~RTir%o121tT9|CC#-W-h7u(+I!!K_=?b+(zZFW1;`5E3bHr z=KFjjMVf#f*wFs*ah;V2GV|H`jPmBv$eJ(K|LR3e?;gm#>fIH{z;+GbjLhaJ&zD1Rp;&mOE z&tuKVr!<4uh10xgyrbUwjPZ^7*LKX7?y#7U>#v`ey7_;^!8Vl^dyH`?9Ix0Q2)yqv z)ShzS`dcozxQ@*nY=Gcm{vpi_-U7h+J@;-UB~LA`>XataO)Q;V<1H+^R8PxeUT=Iw z!^~tEcF4xl+k<%~xA0hczvx4{K=)KEW{uS8Awy2itc4+GshnoC7`=!nxH#)71Yc=7Q&Y?%j0ig4R0TgM5j7zzjMHA{ z1k;eH!Qkg-V;WwrwP|rLjx(de)P_0@T5CpJasoQ#q#TEWzM^FM#pBe`2Xo_R6poFi z!{WmJs=SL{+Apd~hlf29$4Bd=$+U-VJw#_{Ik!;i1_TO)19=ZDM`6pt-kHb~RTV^! zr)#-6QXgDP2~$nHq6YGd>+x9dTGFvHmo=w{-un1N+8p_);J8MCc2a#goI+^+vPWC^TsdPL(F5r`1McbJs%#zuT6wR*Sq7@W}!Lqb@ATv(=RZd?@2)AR`!l$ip zUU5!)zu6ozBZ7f#yoJaqc>`t*bEf9&Xa#BO z6&QRmEEe@a8>~!Zi*26xHCelQB$i)t*JIW4<&`FWx_-ApJSsTUO?!f8c;c@QU2ezp zO2r%`n30Q7dSHn9x*Sll&2_=cEr8s6vlutZf~_X)p`xejtxknDj4$2dKJBk+S9OC# z80By}^dISa&z^&ca$86`yk}?BHH;^k#Rj2flO+{C9r!k)!sdPnQL5$sdr;r05UxIb z+PC<~QgkMqip$<8K5mJ7xcZduYEu|WQ&h~HDV4{GQ3C&rF>ii4T@7-_-%_OR95u?! zSwB*qMQu06?8uX+EPLS;=Ny3)TYpH{hS+f;;f>CMDOw`vo87Fv9#<6fX7$iyJ#gZ< zY<${adhI}bbfQJ^6H@p4-~s?RfbDbS-%DJa>^ICw^mp+ z?3g>(KQzAy{{#uJ!TbT}T1bP(#8a^+A}5CwFcw#ay$B(^(P5Y^E+Y8&ranZ=UhXqw;R$ z{~;Uf@ulcFLWHnEA2m>(i5IawCt=rp)_v1K)S#9(;~ahB?p1FxPr&mB z1A9_mA6xLmpeu%q^0}UN$ejEbgB)%SHhcan8%Lkud9cQ^&JPLVm}^TX6SBL~v#=Y9 zgV&b(x;D=1jGjykzCY9d{j@K&jZYM4OX%OaKqg8TzI^eR&;s^|2Ip7g_nw~$E-ai+ ze^A+VSR()F(%vtcUYmB^r**a=axe*sV?83xNWqPqrIpWKbx}0N45cf*1@y5g8xNri zx`>^cgKMWUQi{6@2SlnSr-RInt&NB1$km^Ii~%Gr&GtlBYpKvw^Fm>)Tzab(26%52E!g8Tan`K z1P_!(7X)p%Z|vL*Y-YL&W_wI!l^+|7q!-?(6i&5Ml0*%B)zWVOSugTYbEDjIsClfc@XS@03txeU}snN z%rAbg{N_DipI?40EM|10(F!uAL4L`n=f(>beFYkN707DIDK;bRrQeerKjt2>e@iJo z2HKoX9qmu1C&PPC7dRn4>|T+o!@+PLqr-iLR59H_@KhpR2tl?Uv??lhTj5Egz4MVs zZ1QY4yH&uf_Y!DpFrG(lY!uiTbix&@l#!+Gl>DTWX)*uEI^H1Rrl{N6_xJYSzu95e zx-zoqTWfX5krsa|W;Gp`ztq6UH)xqKT^<*U+Y2%&-BDi?rWC(Oo}Je%uN)hb4)<_1 zE2HsOQ`RcDB`HrC96igltj~u^DijK#Y%F%H+b(<{NIEoly`2P6U*UNRs3@Q6&s`%8 zlTME_>uV^vulon!GMktm;rA-18u-B?PrV0h2?xwU`Q)GSVWY0qes{@+E4uy5H&Rlo zBkt~8OMQR2md^GbRg!J`=)9nCDTbU_q!s z$+^KX;3S~5k9Dff3+}I!-Qa;_3OYG98Y{uORf;dO9pY$)RnAf1>}(F}IahXLB$w)M zSS*zE-@aN7dP0a~l-Y$ZZbWw3OVTwAH|FDg;>ybu-;HI6gs_1gy2LBKh~C!@D_?Mb zkdAkdc~jtSF|s9sL{(vD?Kx2nn)MS13s-N?G_!DtDYMgwKMsg)mP%VH2(RGj(IXxR z@ly{?=PuZ)eY&dZu-`psRfKVV>JvC&1c;~@!j_~I_+S`Q+1acE7ls(an=?#8Jp5Iw z6>AA^We|MN_;)uk5Tr=Sf56FJaD_25TJ#+Ug}39&o-4YlQ7j{cyZs!Ch)M!ox#t&CCd7(||xC z5^ahi;>{uhLd}4t=4JqU$H>qC6b>D(i$n)u2@t&*L5-d+HV~rc1$Hxc3w1)@z+Q+Z zp*^B6!BEjSlue+Xql2z}q-`WV6ps!M(2c~05XiQX5WTm~ZI%4C*i29Ntx7l!qIY&X zs_s>{uXUY>B(yHr6l{Vrw*cx|*_Z;sU@I$&)4CSsKua@opcxQk0<^KUw6rxh*ZqU^ zl+j3m7~3n(E`P*Q&LDb!Y$_rm!ZZS8N+boD0c~t-%*-v!EG$fv8Ybi@LU=%=34yHt zrw3;=8AZZ|hGU5Y-EEHnNFpU1qNmLCUt7S3y1Bh`{BNs;$8YcJtu{IQ3i_`!{;f6{ z78Qy%yMiVYDI^qHsjmO0v9fmmInbYqfv9(Sp%hZc+kFP2%+MidJem+rRvsbnZAQw> zZQV&&%rC=dv>w1%E_wgEez0Rhd=KyA#e|FpeGAcqGKQ0VP= z+qT$uwjh`PXlv_4LI;EsNiZTYNE(WE%x8Uce1i^K5OA(19Wma3$*|O&std9K%rJ(3oDB=AgDD^?+@F+ zzt!r`eM7-Okdrmo5(xf(Y|WI(1fp!ivEd=;Kfrl5B>WF91iK9o+mHZ4kg^;mfoMzs zB_v$W;T$@E5{|`CLUeBggoZ}xMiB7<1YImaIStoU0+LL@q%PpbF;cAog+{K*{vzz*f-?b;V_*FISv)JbmOH%6LSA}#k`jhNh$0S>)wZ$RKb zlo~2qV!-!+T|2i)IqurCOZnTod)Kbrd-v_xvv<#)ef#%)q@;cO_aE3M<#_Pmfdi`B zbmSeW?ApD1-`;(P_wPS^A;DmztGwq62qluyN- zZ@+K%vOVnCyKlG3j$J!d-qJgJ9nw>-t*~a zdx2*!2dEu6voB2VQO6T?%D3rA(sz%;;TGqvQLOr%zuwqb4xeI* z30lov1XmG)(uGXsaM&>zOpteGdd8BMd<)QMRh37}+KFwgx*25LX*vw2BRsh!_-Jdk z0S9yH!bDz6j0dN(mMj^IIqB}ar=JSG20GR3(#czE?5>u?*{H*_lxUA}Q?~-qZHV>$i@RT6kuqDfU+hdvwpjop#r{GP7Rh zG6vj<%AbL`RA22tOrNGv>K|FTR?&$~eZ}oTlo~o=6v4d=bki_q8(=#y7|Sj`|F=#c zuPZ_lS_XM+9MMaNwm)r0K~#DD7+zSbDrMv~(7&`8Gn%1ilTLE;3K--dF@@zsOY{?$ z3x{%K!uw2cgIK8Pw5yq4b+q2F(Yc>H z!&<||Cx%K!<*;b$#cnXpvnuxvq^fYdp}Y9%`*aSlFmkhxq$A|Tvb{PN>WT+8Yr|?D z&BoK6yrm~5^5j2lM${*yHm`c7-yd{osTfKjw1AFIFxWYZc??c=a(423Jq$PdbHVip zM5E)Abu;2C!8LyZ*rAYV`?8H)s;b$VXInJRlReFvODi-&PWYvHRCi1LV#4~(T(OB5 z&a@CVu8v@(5?DS_%_!mhD7ahy;wul)5 zF^-k6keR`{%+ri2IF-U-r(ig2Lw0s2r|^-Ue!Xs8?47X2(e-{~W9?`ef$>B-=sdZu z1&1<$n;}=4Hai#QZF48*>`ppmL}#jNouImanXe@|=g|&n)%|7&HQ4$W_sCYo2PIy* zo9(l4j}1l;-dFENr+E<`j{}DclUWd0xXAJ2iwVak;@O1zI-~2i^G?;uwH7XLmLb*m zf{kY3@x!%8hfwue9%>HAuE0tC5V8Mci_I6g)h*;0a_@`?J}I}031*ax^o%vC7GG)Z z>;tnt8HkPfF`ylVt7AXx%f3bXZ3Jke5iBf=uEDtv2w!(x0`iMMCDw{8HK}jyWRzW= zDD}>rU=EHSFMzFG;4miWG*va=!$^a0>lwtiwch=k=w8A{P$qc}1e&@kSb5S;k~i@` z>Pl_nNF`eU+;6QTeV@zV4#`h%C?JWXtY?)yLpphB~& zUtaiWGdE_$qMDxvChlD}rfp>1T#VLiJJt`AS0*l0+pYUwYkER6+sE#ztFDmYLM5j! zi$1xwd)IN* z6&MA#@~aH{pu;$@05Z7xc~71ASqUT>9v>HTMlS*D+;X&tb=nO9k9XCFv-NaJ!`fgn z+}(NHVnRtT0Vy34Ip~~4ooR#BOT8Yt50$so<2T}~XZm{k6b@T}F_YtqT{Qdl+lTx5 zb<-fv`6upr3A0%44^UWJcTZ)3Ox&9E1mY_zT!1@ z6&CPy4#VTFg&kjF^M>l>KfJCY)2|ad$2(=>c)Wl17NEPa1I+ra;zrU6{1;?_|IEpo zB0j#J*2nuQs#9uwlrbBP)5>@mHIYwQPXr-)Z~XB1O7$F*=1Ik81t(`0niJ z24B~>R8ix=UvaL_4k3BL6dI7PzGu|9iUygk#90WAI4^zBQsDHbBpH(n?%HK?z>oBtF|NJGtsvh(cdMyvK ze)VZ^&3UTq>DvDW3%`PS0lT#=N9TNU5!d?9Q&}ZjfI+fjFK+97LzRrwV zBI(|^m}=5$@fg`FVR~RTir%o121tT9|CC#-W-h7u(+I!!K_=?b+(zZFW1;`5E3bHr z=KFjjMVf#f*wFs*ah;V2GV|H`jPmBv$eJ(K|LR3e?;gm#>fIH{z;+GbjLhaJ&zD1Rp;&mOE z&tuKVr!<4uh10xgyrbUwjPZ^7*LKX7?y#7U>#v`ey7_;^!8Vl^dyH`?9Ix0Q2)yqv z)ShzS`dcozxQ@*nY=Gcm{vpi_-U7h+J@;-UB~LA`>XataO)Q;V<1H+^R8PxeUT=Iw z!^~tEcF4xl+k<%~xA0hczvx4{K=)KEW{uS8Awy2itc4+GshnoC7`=!nxH#)71Yc=7Q&Y?%j0ig4R0TgM5j7zzjMHA{ z1k;eH!Qkg-V;WwrwP|rLjx(de)P_0@T5CpJasoQ#q#TEWzM^FM#pBe`2Xo_R6poFi z!{WmJs=SL{+Apd~hlf29$4Bd=$+U-VJw#_{Ik!;i1_TO)19=ZDM`6pt-kHb~RTV^! zr)#-6QXgDP2~$nHq6YGd>+x9dTGFvHmo=w{-un1N+8p_);J8MCc2a#goI+^+vPWC^TsdPL(F5r`1McbJs%#zuT6wR*Sq7@W}!Lqb@ATv(=RZd?@2)AR`!l$ip zUU5!)zu6ozBZ7f#yoJaqc>`t*bEf9&Xa#BO z6&QRmEEe@a8>~!Zi*26xHCelQB$i)t*JIW4<&`FWx_-ApJSsTUO?!f8c;c@QU2ezp zO2r%`n30Q7dSHn9x*Sll&2_=cEr8s6vlutZf~_X)p`xejtxknDj4$2dKJBk+S9OC# z80By}^dISa&z^&ca$86`yk}?BHH;^k#Rj2flO+{C9r!k)!sdPnQL5$sdr;r05UxIb z+PC<~QgkMqip$<8K5mJ7xcZduYEu|WQ&h~HDV4{GQ3C&rF>ii4T@7-_-%_OR95u?! zSwB*qMQu06?8uX+EPLS;=Ny3)TYpH{hS+f;;f>CMDOw`vo87Fv9#<6fX7$iyJ#gZ< zY<${adhI}bbfQJ^6H@p4-~s?RfbDbS-%DJa>^ICw^mp+ z?3g>(KQzAy{{#uJ!TbT}T1bP(#8a^+A}5CwFcw#ay$B(^(P5Y^E+Y8&ranZ=UhXqw;R$ z{~;Uf@ulcFLWHnEA2m>(i5IawCt=rp)_v1K)S#9(;~ahB?p1FxPr&mB z1A9_mA6xLmpeu%q^0}UN$ejEbgB)%SHhcan8%Lkud9cQ^&JPLVm}^TX6SBL~v#=Y9 zgV&b(x;D=1jGjykzCY9d{j@K&jZYM4OX%OaKqg8TzI^eR&;s^|2Ip7g_nw~$E-ai+ ze^A+VSR()F(%vtcUYmB^r**a=axe*sV?83xNWqPqrIpWKbx}0N45cf*1@y5g8xNri zx`>^cgKMWUQi{6@2SlnSr-RInt&NB1$km^Ii~%Gr&GtlBYpKvw^Fm>)Tzab(26%52E!g8Tan`K z1P_!(7X)p%Z|vL*Y-YL&W_wI!l^+|7q!-?(6i&5Ml0*%B)zWVOSugTYbEDjIsClfc@XS@03txeU}snN z%rAbg{N_DipI?40EM|10(F!uAL4L`n=f(>beFYkN707DIDK;bRrQeerKjt2>e@iJo z2HKoX9qmu1C&PPC7dRn4>|T+o!@+PLqr-iLR59H_@KhpR2tl?Uv??lhTj5Egz4MVs zZ1QY4yH&uf_Y!DpFrG(lY!uiTbix&@l#!+Gl>DTWX)*uEI^H1Rrl{N6_xJYSzu95e zx-zoqTWfX5krsa|W;Gp`ztq6UH)xqKT^<*U+Y2%&-BDi?rWC(Oo}Je%uN)hb4)<_1 zE2HsOQ`RcDB`HrC96igltj~u^DijK#Y%F%H+b(<{NIEoly`2P6U*UNRs3@Q6&s`%8 zlTME_>uV^vulon!GMktm;rA-18u-B?PrV0h2?xwU`Q)GSVWY0qes{@+E4uy5H&Rlo zBkt~8OMQR2md^GbRg!J`=)9nCDTbU_q!s z$+^KX;3S~5k9Dff3+}I!-Qa;_3OYG98Y{uORf;dO9pY$)RnAf1>}(F}IahXLB$w)M zSS*zE-@aN7dP0a~l-Y$ZZbWw3OVTwAH|FDg;>ybu-;HI6gs_1gy2LBKh~C!@D_?Mb zkdAkdc~jtSF|s9sL{(vD?Kx2nn)MS13s-N?G_!DtDYMgwKMsg)mP%VH2(RGj(IXxR z@ly{?=PuZ)eY&dZu-`psRfKVV>JvC&1c;~@!j_~I_+S`Q+1acE7ls(an=?#8Jp5Iw z6>AA^We|MN_;)uk5Tr=Sf56FJaD_25TJ#+Ug}39&o-4YlQ7j{cyZs!Ch)M!ox#t&CCd7(||xC z5^ahi;>{uhLd}4t=4JqU$H>qC6b>D(i$n)u2@t&*L5-d+HV~rc1$Hxc3w1)@z+Q+Z zp*^B6!BEjSlue+Xql2z}q-`WV6ps!M(2c~05XiQX5WTm~ZI%4C*i29Ntx7l!qIY&X zs_s>{uXUY>B(yHr6l{Vrw*cx|*_Z;sU@I$&)4CSsKua@opcxQk0<^KUw6rxh*ZqU^ zl+j3m7~3n(E`P*Q&LDb!Y$_rm!ZZS8N+boD0c~t-%*-v!EG$fv8Ybi@LU=%=34yHt zrw3;=8AZZ|hGU5Y-EEHnNFpU1qNmLCUt7S3y1Bh`{BNs;$8YcJtu{IQ3i_`!{;f6{ z78Qy%yMiVYDI^qHsjmO0v9fmmInbYqfv9(Sp%hZc+kFP2%+MidJem+rRvsbnZAQw> zZQV&&%rC=dv>w1%E_wgEez0Rhd=KyA#e|FpeGAcqGKQ0VP= z+qT$uwjh`PXlv_4LI;EsNiZTYNE(WE%x8Uce1i^K5OA(19Wma3$*|O&std9K%rJ(3oDB=AgDD^?+@F+ zzt!r`eM7-Okdrmo5(xf(Y|WI(1fp!ivEd=;Kfrl5B>WF91iK9o+mHZ4kg^;mfoMzs zB_v$W;T$@E5{|`CLUeBggoZ}xMiB7<1YImaIStoU0+LL@q%PpbF;cAog+{K*{vzz*f-?b;V_*FISv)JbmOH%6LSA}#k`jhNh$0S>)wZ$RKb zlo~2qV!-!+T|2i)IqurCOZnTod)Kbrd-v_xvv<#)ef#%)q@;cO_aE3M<#_Pmfdi`B zbmSeW?ApD1-`;(P_wPS^A;DmztGwq62qluyN- zZ@+K%vOVnCyKlG3j$J!d-qJgJ9nw>-t*~a zdx2*!2dEu6voB2VQO6T?%D3rA(sz%;;TGqvQLOr%zuwqb4xeI* z30lov1XmG)(uGXsaM&>zOpteGdd8BMd<)QMRh37}+KFwgx*25LX*vw2BRsh!_-Jdk z0S9yH!bDz6j0dN(mMj^IIqB}ar=JSG20GR3(#czE?5>u?*{H*_lxUA}Q?~-qZHV>$i@RT6kuqDfU+hdvwpjop#r{GP7Rh zG6vj<%AbL`RA22tOrNGv>K|FTR?&$~eZ}oTlo~o=6v4d=bki_q8(=#y7|Sj`|F=#c zuPZ_lS_XM+9MMaNwm)r0K~#DD7+zSbDrMv~(7&`8Gn%1ilTLE;3K--dF@@zsOY{?$ z3x{%K!uw2cgIK8Pw5yq4b+q2F(Yc>H z!&<||Cx%K!<*;b$#cnXpvnuxvq^fYdp}Y9%`*aSlFmkhxq$A|Tvb{PN>WT+8Yr|?D z&BoK6yrm~5^5j2lM${*yHm`c7-yd{osTfKjw1AFIFxWYZc??c=a(423Jq$PdbHVip zM5E)Abu;2C!8LyZ*rAYV`?8H)s;b$VXInJRlReFvODi-&PWYvHRCi1LV#4~(T(OB5 z&a@CVu8v@(5?DS_%_!mhD7ahy;wul)5 zF^-k6keR`{%+ri2IF-U-r(ig2Lw0s2r|^-Ue!Xs8?47X2(e-{~W9?`ef$>B-=sdZu z1&1<$n;}=4Hai#QZF48*>`ppmL}#jNouImanXe@|=g|&n)%|7&HQ4$W_sCYo2PIy* zo9(l4j}1l;-dFENr+E<`j{}DclUWd0xXAJ2iwVak;@O1zI-~2i^G?;uwH7XLmLb*m zf{kY3@x!%8hfwue9%>HAuE0tC5V8Mci_I6g)h*;0a_@`?J}I}031*ax^o%vC7GG)Z z>;tnt8HkPfF`ylVt7AXx%f3bXZ3Jke5iBf=uEDtv2w!(x0`iMMCDw{8HK}jyWRzW= zDD}>rU=EHSFMzFG;4miWG*va=!$^a0>lwtiwch=k=w8A{P$qc}1e&@kSb5S;k~i@` z>Pl_nNF`eU+;6QTeV@zV4#`h%C?JWXtY?)yLpphB~& zUtaiWGdE_$qMDxvChlD}rfp>1T#VLiJJt`AS0*l0+pYUwYkER6+sE#ztFDmYLM5j! zi$1xwd)IN* z6&MA#@~aH{pu;$@05Z7xc~71ASqUT>9v>HTMlS*D+;X&tb=nO9k9XCFv-NaJ!`fgn z+}(NHVnRtT0Vy34Ip~~4ooR#BOT8Yt50$so<2T}~XZm{k6b@T}F_YtqT{Qdl+lTx5 zb<-fv`6upr3A0%44^UWJcTZ)3Ox&9E1mY_zT!1@ z6&CPy4#VTFg&kjF^M>l>KfJCY)2|ad$2(=>c)Wl17NEPa1I+ra;zrU6{1;?_|IEpo zB0j#J*2nuQs#9uwlrbBP)5>@mHIYwQPXr-)Z~XB1O7$F*=1Ik81t(`0niJ z24B~>R8ix=UvaL_4k3BL6dI7PzGu|9iUygk#90WAI4^zBQsDHbBpH(n?%HK?z>oBtF|NJGtsvh(cdMyvK ze)VZ^&3UTq>DvDW3%`PS0lT#=N9TNU5!d?9Q&}ZjfI+fjFK+97LzRrwV zBI(|^m}=5$@fg`FVR~RTir%o121tT9|CC#-W-h7u(+I!!K_=?b+(zZFW1;`5E3bHr z=KFjjMVf#f*wFs*ah;V2GV|H`jPmBv$eJ(K|LR3e?;gm#>fIH{z;+GbjLhaJ&zD1Rp;&mOE z&tuKVr!<4uh10xgyrbUwjPZ^7*LKX7?y#7U>#v`ey7_;^!8Vl^dyH`?9Ix0Q2)yqv z)ShzS`dcozxQ@*nY=Gcm{vpi_-U7h+J@;-UB~LA`>XataO)Q;V<1H+^R8PxeUT=Iw z!^~tEcF4xl+k<%~xA0hczvx4{K=)KEW{uS8Awy2itc4+GshnoC7`=!nxH#)71Yc=7Q&Y?%j0ig4R0TgM5j7zzjMHA{ z1k;eH!Qkg-V;WwrwP|rLjx(de)P_0@T5CpJasoQ#q#TEWzM^FM#pBe`2Xo_R6poFi z!{WmJs=SL{+Apd~hlf29$4Bd=$+U-VJw#_{Ik!;i1_TO)19=ZDM`6pt-kHb~RTV^! zr)#-6QXgDP2~$nHq6YGd>+x9dTGFvHmo=w{-un1N+8p_);J8MCc2a#goI+^+vPWC^TsdPL(F5r`1McbJs%#zuT6wR*Sq7@W}!Lqb@ATv(=RZd?@2)AR`!l$ip zUU5!)zu6ozBZ7f#yoJaqc>`t*bEf9&Xa#BO z6&QRmEEe@a8>~!Zi*26xHCelQB$i)t*JIW4<&`FWx_-ApJSsTUO?!f8c;c@QU2ezp zO2r%`n30Q7dSHn9x*Sll&2_=cEr8s6vlutZf~_X)p`xejtxknDj4$2dKJBk+S9OC# z80By}^dISa&z^&ca$86`yk}?BHH;^k#Rj2flO+{C9r!k)!sdPnQL5$sdr;r05UxIb z+PC<~QgkMqip$<8K5mJ7xcZduYEu|WQ&h~HDV4{GQ3C&rF>ii4T@7-_-%_OR95u?! zSwB*qMQu06?8uX+EPLS;=Ny3)TYpH{hS+f;;f>CMDOw`vo87Fv9#<6fX7$iyJ#gZ< zY<${adhI}bbfQJ^6H@p4-~s?RfbDbS-%DJa>^ICw^mp+ z?3g>(KQzAy{{#uJ!TbT}T1bP(#8a^+A}5CwFcw#ay$B(^(P5Y^E+Y8&ranZ=UhXqw;R$ z{~;Uf@ulcFLWHnEA2m>(i5IawCt=rp)_v1K)S#9(;~ahB?p1FxPr&mB z1A9_mA6xLmpeu%q^0}UN$ejEbgB)%SHhcan8%Lkud9cQ^&JPLVm}^TX6SBL~v#=Y9 zgV&b(x;D=1jGjykzCY9d{j@K&jZYM4OX%OaKqg8TzI^eR&;s^|2Ip7g_nw~$E-ai+ ze^A+VSR()F(%vtcUYmB^r**a=axe*sV?83xNWqPqrIpWKbx}0N45cf*1@y5g8xNri zx`>^cgKMWUQi{6@2SlnSr-RInt&NB1$km^Ii~%Gr&GtlBYpKvw^Fm>)Tzab(26%52E!g8Tan`K z1P_!(7X)p%Z|vL*Y-YL&W_wI!l^+|7q!-?(6i&5Ml0*%B)zWVOSugTYbEDjIsClfc@XS@03txeU}snN z%rAbg{N_DipI?40EM|10(F!uAL4L`n=f(>beFYkN707DIDK;bRrQeerKjt2>e@iJo z2HKoX9qmu1C&PPC7dRn4>|T+o!@+PLqr-iLR59H_@KhpR2tl?Uv??lhTj5Egz4MVs zZ1QY4yH&uf_Y!DpFrG(lY!uiTbix&@l#!+Gl>DTWX)*uEI^H1Rrl{N6_xJYSzu95e zx-zoqTWfX5krsa|W;Gp`ztq6UH)xqKT^<*U+Y2%&-BDi?rWC(Oo}Je%uN)hb4)<_1 zE2HsOQ`RcDB`HrC96igltj~u^DijK#Y%F%H+b(<{NIEoly`2P6U*UNRs3@Q6&s`%8 zlTME_>uV^vulon!GMktm;rA-18u-B?PrV0h2?xwU`Q)GSVWY0qes{@+E4uy5H&Rlo zBkt~8OMQR2md^GbRg!J`=)9nCDTbU_q!s z$+^KX;3S~5k9Dff3+}I!-Qa;_3OYG98Y{uORf;dO9pY$)RnAf1>}(F}IahXLB$w)M zSS*zE-@aN7dP0a~l-Y$ZZbWw3OVTwAH|FDg;>ybu-;HI6gs_1gy2LBKh~C!@D_?Mb zkdAkdc~jtSF|s9sL{(vD?Kx2nn)MS13s-N?G_!DtDYMgwKMsg)mP%VH2(RGj(IXxR z@ly{?=PuZ)eY&dZu-`psRfKVV>JvC&1c;~@!j_~I_+S`Q+1acE7ls(an=?#8Jp5Iw z6>AA^We|MN_;)uk5Tr=Sf56FJaD_25TJ#+Ug}39&o-4YlQ7j{c Date: Thu, 25 Jul 2024 16:56:31 +0900 Subject: [PATCH 09/32] =?UTF-8?q?feat=20:=20=EC=9E=90=EB=8F=99=ED=99=94=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/edufocus/edufocus/user/controller/UserController.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/backend/src/main/java/com/edufocus/edufocus/user/controller/UserController.java b/backend/src/main/java/com/edufocus/edufocus/user/controller/UserController.java index f27e42d..9f5e6d7 100644 --- a/backend/src/main/java/com/edufocus/edufocus/user/controller/UserController.java +++ b/backend/src/main/java/com/edufocus/edufocus/user/controller/UserController.java @@ -65,10 +65,8 @@ public class UserController { String accessToken = jwtUtil.createAccessToken(String.valueOf(loginUser.getId())); String refreshToken = jwtUtil.createRefreshToken(String.valueOf(loginUser.getId())); - // 발급받은 refresh token 을 DB에 저장. userService.saveRefreshToken(loginUser.getId(), refreshToken); - // JSON 으로 token 전달. System.out.println(accessToken); resultMap.put("access-token", accessToken); // resultMap.put("refresh-token", refreshToken); @@ -82,7 +80,7 @@ public class UserController { response.addCookie(refreshCookie); - // 쿠키저장 + // 쿠키저장f status = HttpStatus.CREATED; } else { resultMap.put("message", "아이디 또는 패스워드를 확인해 주세요."); From 491573592b9fce71e9ae1ab973ea0b114f90f337 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A0=95=EB=AF=BC?= Date: Fri, 26 Jul 2024 12:36:05 +0900 Subject: [PATCH 10/32] =?UTF-8?q?feat:=20user=20login=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../user/controller/UserController.java | 26 ++++++------------- 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/backend/src/main/java/com/edufocus/edufocus/user/controller/UserController.java b/backend/src/main/java/com/edufocus/edufocus/user/controller/UserController.java index 9f5e6d7..12e4b56 100644 --- a/backend/src/main/java/com/edufocus/edufocus/user/controller/UserController.java +++ b/backend/src/main/java/com/edufocus/edufocus/user/controller/UserController.java @@ -41,17 +41,7 @@ public class UserController { return ResponseEntity.ok("임시 비밀번호가 이메일로 전송되었습니다."); } -// -// @PostMapping("/login") -// public ResponseEntity login(@RequestBody User user) { -// try { -// User loggedInUser = userService.login(user); -// return ResponseEntity.ok(loggedInUser); -// -// } catch (Exception e) { -// throw new RuntimeException(e); -// } - // } + @Operation(summary = "로그인", description = "아이디와 비밀번호를 이용하여 로그인 처리.") @PostMapping("/login") @@ -59,6 +49,9 @@ public class UserController { @RequestBody @Parameter(description = "로그인 시 필요한 회원정보(아이디, 비밀번호).", required = true) User user, HttpServletResponse response) { Map resultMap = new HashMap<>(); HttpStatus status = HttpStatus.ACCEPTED; + + String name= user.getName(); + resultMap.put("name",name); try { User loginUser = userService.login(user); if (loginUser != null) { @@ -69,9 +62,9 @@ public class UserController { System.out.println(accessToken); resultMap.put("access-token", accessToken); - // resultMap.put("refresh-token", refreshToken); - // 쿠키 저장 + + Cookie refreshCookie = new Cookie("refresh-token", refreshToken); refreshCookie.setPath("/"); refreshCookie.setHttpOnly(true); @@ -80,7 +73,7 @@ public class UserController { response.addCookie(refreshCookie); - // 쿠키저장f + status = HttpStatus.CREATED; } else { resultMap.put("message", "아이디 또는 패스워드를 확인해 주세요."); @@ -98,12 +91,9 @@ public class UserController { public ResponseEntity> getInfo( @PathVariable("userId") @Parameter(description = "인증할 회원의 아이디.", required = true) Long userId, HttpServletRequest request) { - //logger.debug("userId : {} ", userId); String id = String.valueOf(userId); - System.out.println("!>>>>>>>>>>>>>>>>>>>>>>>>"); - System.out.println(id); - System.out.println(id.getClass().getName()); + Map resultMap = new HashMap<>(); HttpStatus status = HttpStatus.ACCEPTED; if (jwtUtil.checkToken(request.getHeader("Authorization"))) { From c4373a24af38750ca7a31904ab2d7ccd63341227 Mon Sep 17 00:00:00 2001 From: kgc9007 Date: Fri, 26 Jul 2024 12:51:02 +0900 Subject: [PATCH 11/32] =?UTF-8?q?feat:=20Lecture=20=EA=B0=95=EC=9D=98=20?= =?UTF-8?q?=EC=83=81=EC=84=B8=EB=B3=B4=EA=B8=B0=EC=8B=9C=20=EA=B0=95?= =?UTF-8?q?=EC=9D=98=20=EC=8B=9C=EA=B0=84=20=EC=A1=B0=ED=9A=8C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../edufocus/edufocus/lecture/entity/Lecture.java | 10 +++++++--- .../lecture/entity/LectureCreateRequest.java | 4 +++- .../lecture/entity/LectureDetailResponse.java | 4 +++- .../lecture/service/LectureServiceImpl.java | 13 +++++++++---- 4 files changed, 22 insertions(+), 9 deletions(-) diff --git a/backend/src/main/java/com/edufocus/edufocus/lecture/entity/Lecture.java b/backend/src/main/java/com/edufocus/edufocus/lecture/entity/Lecture.java index 24adc42..0a9168a 100644 --- a/backend/src/main/java/com/edufocus/edufocus/lecture/entity/Lecture.java +++ b/backend/src/main/java/com/edufocus/edufocus/lecture/entity/Lecture.java @@ -15,7 +15,8 @@ import java.util.Date; @AllArgsConstructor public class Lecture { - @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) @Column private Long id; @@ -29,6 +30,9 @@ public class Lecture { @Lob private String description; + @Lob + private String plan; + @Column private String image; @@ -40,8 +44,8 @@ public class Lecture { @Temporal(TemporalType.DATE) private Date endDate; - @Lob - private String plan; + @Column + private String time; @Column private boolean online; diff --git a/backend/src/main/java/com/edufocus/edufocus/lecture/entity/LectureCreateRequest.java b/backend/src/main/java/com/edufocus/edufocus/lecture/entity/LectureCreateRequest.java index d76afbe..dfd02fd 100644 --- a/backend/src/main/java/com/edufocus/edufocus/lecture/entity/LectureCreateRequest.java +++ b/backend/src/main/java/com/edufocus/edufocus/lecture/entity/LectureCreateRequest.java @@ -15,10 +15,12 @@ public class LectureCreateRequest { private String description; + private String plan; + private Date startDate; private Date endDate; - private String plan; + private String time; } diff --git a/backend/src/main/java/com/edufocus/edufocus/lecture/entity/LectureDetailResponse.java b/backend/src/main/java/com/edufocus/edufocus/lecture/entity/LectureDetailResponse.java index 9c14a49..0a5629a 100644 --- a/backend/src/main/java/com/edufocus/edufocus/lecture/entity/LectureDetailResponse.java +++ b/backend/src/main/java/com/edufocus/edufocus/lecture/entity/LectureDetailResponse.java @@ -21,13 +21,15 @@ public class LectureDetailResponse { private String description; + private String plan; + private String image; private Date startDate; private Date endDate; - private String plan; + private String time; private boolean online; diff --git a/backend/src/main/java/com/edufocus/edufocus/lecture/service/LectureServiceImpl.java b/backend/src/main/java/com/edufocus/edufocus/lecture/service/LectureServiceImpl.java index 73267b9..52a8824 100644 --- a/backend/src/main/java/com/edufocus/edufocus/lecture/service/LectureServiceImpl.java +++ b/backend/src/main/java/com/edufocus/edufocus/lecture/service/LectureServiceImpl.java @@ -43,9 +43,10 @@ public class LectureServiceImpl implements LectureService { .user(user) .title(lectureCreateRequest.getTitle()) .description(lectureCreateRequest.getDescription()) + .plan(lectureCreateRequest.getPlan()) .startDate(lectureCreateRequest.getStartDate()) .endDate(lectureCreateRequest.getEndDate()) - .plan(lectureCreateRequest.getPlan()) + .time(lectureCreateRequest.getTime()) .build(); if (image != null && !image.isEmpty()) { @@ -81,14 +82,17 @@ public class LectureServiceImpl implements LectureService { if (lectureCreateRequest.getDescription() != null) { lecture.setDescription(lectureCreateRequest.getDescription()); } + if (lectureCreateRequest.getPlan() != null) { + lecture.setPlan(lectureCreateRequest.getPlan()); + } if (lectureCreateRequest.getStartDate() != null) { lecture.setStartDate(lectureCreateRequest.getStartDate()); } if (lectureCreateRequest.getEndDate() != null) { lecture.setEndDate(lectureCreateRequest.getEndDate()); } - if (lectureCreateRequest.getPlan() != null) { - lecture.setPlan(lectureCreateRequest.getPlan()); + if (lectureCreateRequest.getTime() != null) { + lecture.setTime(lectureCreateRequest.getTime()); } lectureRepository.save(lecture); @@ -167,10 +171,11 @@ public class LectureServiceImpl implements LectureService { .id(lecture.get().getId()) .title(lecture.get().getTitle()) .description(lecture.get().getDescription()) + .plan(lecture.get().getPlan()) .image(lecture.get().getImage()) .startDate(lecture.get().getStartDate()) .endDate(lecture.get().getEndDate()) - .plan(lecture.get().getPlan()) + .time(lecture.get().getTime()) .online(lecture.get().isOnline()) .teacherName(lecture.get().getUser().getName()) .status(userStatus) From 37fd8cea68dedc8609bfd5d61c5c737ebfda3ad9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A0=95=EB=AF=BC?= Date: Fri, 26 Jul 2024 13:22:41 +0900 Subject: [PATCH 12/32] =?UTF-8?q?feat:=20user=20login,=20qna=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/edufocus/edufocus/qna/entity/QnaResponseDto.java | 3 ++- .../edufocus/edufocus/user/controller/UserController.java | 7 +++++-- .../edufocus/edufocus/user/model/service/UserService.java | 2 +- .../edufocus/user/model/service/UserServiceImpl.java | 7 +++++++ 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/backend/src/main/java/com/edufocus/edufocus/qna/entity/QnaResponseDto.java b/backend/src/main/java/com/edufocus/edufocus/qna/entity/QnaResponseDto.java index 3ef0b6a..ee7c768 100644 --- a/backend/src/main/java/com/edufocus/edufocus/qna/entity/QnaResponseDto.java +++ b/backend/src/main/java/com/edufocus/edufocus/qna/entity/QnaResponseDto.java @@ -14,6 +14,7 @@ import java.util.Date; public class QnaResponseDto { + private Long id; private String title; private String username; private String content; @@ -22,7 +23,7 @@ public class QnaResponseDto { public static QnaResponseDto toEntity(Qna qna) { return new QnaResponseDto( - + qna.getId(), qna.getTitle(), qna.getUser().getName(), qna.getContent(), diff --git a/backend/src/main/java/com/edufocus/edufocus/user/controller/UserController.java b/backend/src/main/java/com/edufocus/edufocus/user/controller/UserController.java index 12e4b56..5b36459 100644 --- a/backend/src/main/java/com/edufocus/edufocus/user/controller/UserController.java +++ b/backend/src/main/java/com/edufocus/edufocus/user/controller/UserController.java @@ -50,11 +50,14 @@ public class UserController { Map resultMap = new HashMap<>(); HttpStatus status = HttpStatus.ACCEPTED; - String name= user.getName(); - resultMap.put("name",name); + + try { User loginUser = userService.login(user); if (loginUser != null) { + + String name = loginUser.getName(); + resultMap.put("name",name); String accessToken = jwtUtil.createAccessToken(String.valueOf(loginUser.getId())); String refreshToken = jwtUtil.createRefreshToken(String.valueOf(loginUser.getId())); diff --git a/backend/src/main/java/com/edufocus/edufocus/user/model/service/UserService.java b/backend/src/main/java/com/edufocus/edufocus/user/model/service/UserService.java index 4f3733a..f661fd1 100644 --- a/backend/src/main/java/com/edufocus/edufocus/user/model/service/UserService.java +++ b/backend/src/main/java/com/edufocus/edufocus/user/model/service/UserService.java @@ -11,5 +11,5 @@ public interface UserService { User userInfo(Long id) throws Exception; void sendEamail(User user) throws Exception; void userCheck(Long id) throws Exception; - + String getUserName(Long id) throws Exception; } diff --git a/backend/src/main/java/com/edufocus/edufocus/user/model/service/UserServiceImpl.java b/backend/src/main/java/com/edufocus/edufocus/user/model/service/UserServiceImpl.java index 2c39b06..554d971 100644 --- a/backend/src/main/java/com/edufocus/edufocus/user/model/service/UserServiceImpl.java +++ b/backend/src/main/java/com/edufocus/edufocus/user/model/service/UserServiceImpl.java @@ -136,6 +136,13 @@ public class UserServiceImpl implements UserService{ sendEamail(user); } } + + @Override + public String getUserName(Long id) throws Exception { + + return userRepository.findById(id).get().getName(); + } + public String getTempPassword() { char[] charSet = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'}; From 9c2abc6d6d651c0c0ce9db406f96534b6dc6bec9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A0=95=EB=AF=BC?= Date: Fri, 26 Jul 2024 16:03:30 +0900 Subject: [PATCH 13/32] =?UTF-8?q?feat:=20qna=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../edufocus/qna/controller/QnaController.java | 4 ++-- .../edufocus/qna/repository/QnaRepository.java | 1 - .../edufocus/qna/service/QnaService.java | 2 +- .../edufocus/qna/service/QnaServiceImpl.java | 18 ++++++++++++++++-- 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/backend/src/main/java/com/edufocus/edufocus/qna/controller/QnaController.java b/backend/src/main/java/com/edufocus/edufocus/qna/controller/QnaController.java index 48cc08a..6ba7628 100644 --- a/backend/src/main/java/com/edufocus/edufocus/qna/controller/QnaController.java +++ b/backend/src/main/java/com/edufocus/edufocus/qna/controller/QnaController.java @@ -106,9 +106,9 @@ public class QnaController { } @GetMapping("/{id}") - public ResponseEntity getQna(@PathVariable Long id) { + public ResponseEntity getQna(@PathVariable Long id) { try{ - Qna findQna= qnaService.getQna(id); + QnaResponseDto findQna= qnaService.getQna(id); return new ResponseEntity<>(findQna, HttpStatus.ACCEPTED); } catch (SQLException e) { diff --git a/backend/src/main/java/com/edufocus/edufocus/qna/repository/QnaRepository.java b/backend/src/main/java/com/edufocus/edufocus/qna/repository/QnaRepository.java index d2cbf7f..9d1c71e 100644 --- a/backend/src/main/java/com/edufocus/edufocus/qna/repository/QnaRepository.java +++ b/backend/src/main/java/com/edufocus/edufocus/qna/repository/QnaRepository.java @@ -13,7 +13,6 @@ import java.util.List; @Repository public interface QnaRepository extends JpaRepository { - List findByLectureId(Long lecturerId); Page findByLectureId(Long lectureId, Pageable pageable); } diff --git a/backend/src/main/java/com/edufocus/edufocus/qna/service/QnaService.java b/backend/src/main/java/com/edufocus/edufocus/qna/service/QnaService.java index 2c84b6d..de97f93 100644 --- a/backend/src/main/java/com/edufocus/edufocus/qna/service/QnaService.java +++ b/backend/src/main/java/com/edufocus/edufocus/qna/service/QnaService.java @@ -16,7 +16,7 @@ public interface QnaService { QnaResponseDto createQna(Long id, QnaRequestDto qnaRequestDto, Long lecture_id) throws SQLException; QnaResponseDto updateQna(Long id,QnaRequestDto qnaRequestDto) throws SQLException; void deleteQna(Long id) throws SQLException; - Qna getQna(Long id) throws SQLException; + QnaResponseDto getQna(Long id) throws SQLException; List getAllQnasByLecture(Long lectureId,int pageNumber) throws SQLException; QnaResponseDto createAnswer(Long id,QnaRequestDto qnaRequestDto) throws SQLException; diff --git a/backend/src/main/java/com/edufocus/edufocus/qna/service/QnaServiceImpl.java b/backend/src/main/java/com/edufocus/edufocus/qna/service/QnaServiceImpl.java index e53f585..bed4103 100644 --- a/backend/src/main/java/com/edufocus/edufocus/qna/service/QnaServiceImpl.java +++ b/backend/src/main/java/com/edufocus/edufocus/qna/service/QnaServiceImpl.java @@ -80,8 +80,22 @@ qnaRepository.deleteById(id); } @Override - public Qna getQna(Long id) { - return null; + public QnaResponseDto getQna(Long id) { + Optional qna; + try { + + qna= qnaRepository.findById(id); + + + } catch (Exception e) { + + throw new RuntimeException("Qna 없음 " + id, e); + } + + + + return QnaResponseDto.toEntity(qna.get()); + } @Override From 97a69ae3af6908dc0850c14b2852d0d21fc3f245 Mon Sep 17 00:00:00 2001 From: yulmam Date: Fri, 26 Jul 2024 16:29:58 +0900 Subject: [PATCH 14/32] =?UTF-8?q?feat=20:=20BoardSummaryDto=20create=20tim?= =?UTF-8?q?e=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../edufocus/EdufocusApplication.java | 2 ++ .../entity/dto/ResponseBoardDetailDto.java | 6 ++--- .../entity/dto/ResponseBoardSummaryDto.java | 3 +++ .../board/entity/dto/ResponseCommentDto.java | 5 ++-- .../edufocus/board/entity/vo/Board.java | 15 +++++++---- .../edufocus/board/entity/vo/Comment.java | 6 ++--- .../src/main/resources/application.properties | 25 +++++++++++++++---- 7 files changed, 44 insertions(+), 18 deletions(-) diff --git a/backend/src/main/java/com/edufocus/edufocus/EdufocusApplication.java b/backend/src/main/java/com/edufocus/edufocus/EdufocusApplication.java index 556bff0..e80c9fd 100644 --- a/backend/src/main/java/com/edufocus/edufocus/EdufocusApplication.java +++ b/backend/src/main/java/com/edufocus/edufocus/EdufocusApplication.java @@ -2,7 +2,9 @@ package com.edufocus.edufocus; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; +@EnableJpaAuditing @SpringBootApplication public class EdufocusApplication { diff --git a/backend/src/main/java/com/edufocus/edufocus/board/entity/dto/ResponseBoardDetailDto.java b/backend/src/main/java/com/edufocus/edufocus/board/entity/dto/ResponseBoardDetailDto.java index 8cf6bc7..e050da7 100644 --- a/backend/src/main/java/com/edufocus/edufocus/board/entity/dto/ResponseBoardDetailDto.java +++ b/backend/src/main/java/com/edufocus/edufocus/board/entity/dto/ResponseBoardDetailDto.java @@ -5,7 +5,7 @@ import lombok.Builder; import lombok.Getter; import lombok.Setter; -import java.time.LocalTime; +import java.time.LocalDateTime; @Builder @Getter @@ -17,6 +17,6 @@ public class ResponseBoardDetailDto { private String title; private String content; private int viewCount; - private LocalTime createdAt; - private LocalTime modifiedAt; + private LocalDateTime createdAt; + private LocalDateTime modifiedAt; } diff --git a/backend/src/main/java/com/edufocus/edufocus/board/entity/dto/ResponseBoardSummaryDto.java b/backend/src/main/java/com/edufocus/edufocus/board/entity/dto/ResponseBoardSummaryDto.java index d3e72a5..bcd1f11 100644 --- a/backend/src/main/java/com/edufocus/edufocus/board/entity/dto/ResponseBoardSummaryDto.java +++ b/backend/src/main/java/com/edufocus/edufocus/board/entity/dto/ResponseBoardSummaryDto.java @@ -5,6 +5,8 @@ import lombok.Builder; import lombok.Getter; import lombok.Setter; +import java.time.LocalDateTime; + @Builder @Getter @Setter @@ -12,4 +14,5 @@ public class ResponseBoardSummaryDto { private long id; private String name; private String title; + private LocalDateTime createdAt; } diff --git a/backend/src/main/java/com/edufocus/edufocus/board/entity/dto/ResponseCommentDto.java b/backend/src/main/java/com/edufocus/edufocus/board/entity/dto/ResponseCommentDto.java index 92156ce..180c6b1 100644 --- a/backend/src/main/java/com/edufocus/edufocus/board/entity/dto/ResponseCommentDto.java +++ b/backend/src/main/java/com/edufocus/edufocus/board/entity/dto/ResponseCommentDto.java @@ -4,6 +4,7 @@ import lombok.Builder; import lombok.Getter; import lombok.Setter; +import java.time.LocalDateTime; import java.time.LocalTime; @Builder @@ -13,6 +14,6 @@ public class ResponseCommentDto { private long id; private String name; private String content; - private LocalTime createAt; - private LocalTime modifiedAt; + private LocalDateTime createAt; + private LocalDateTime modifiedAt; } diff --git a/backend/src/main/java/com/edufocus/edufocus/board/entity/vo/Board.java b/backend/src/main/java/com/edufocus/edufocus/board/entity/vo/Board.java index 6272a17..6b283ec 100644 --- a/backend/src/main/java/com/edufocus/edufocus/board/entity/vo/Board.java +++ b/backend/src/main/java/com/edufocus/edufocus/board/entity/vo/Board.java @@ -8,8 +8,11 @@ import com.edufocus.edufocus.user.model.entity.User; import com.fasterxml.jackson.annotation.JsonAutoDetect; import jakarta.persistence.*; import lombok.*; -import org.hibernate.annotations.CreationTimestamp; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; +import java.time.LocalDateTime; import java.time.LocalTime; import java.util.List; @@ -18,6 +21,7 @@ import java.util.List; @NoArgsConstructor @AllArgsConstructor @Setter +@EntityListeners(AuditingEntityListener.class) @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY) public class Board { @@ -37,11 +41,11 @@ public class Board { @Column(nullable = true) private int viewCount; - @CreationTimestamp - LocalTime createdAt; + @CreatedDate + LocalDateTime createdAt; - @CreationTimestamp - LocalTime modifiedAt; + @LastModifiedDate + LocalDateTime modifiedAt; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id") @@ -60,6 +64,7 @@ public class Board { .id(id) .title(title) .name(user.getUserId()) + .createdAt(createdAt) .build(); } diff --git a/backend/src/main/java/com/edufocus/edufocus/board/entity/vo/Comment.java b/backend/src/main/java/com/edufocus/edufocus/board/entity/vo/Comment.java index 90c7ebf..667f678 100644 --- a/backend/src/main/java/com/edufocus/edufocus/board/entity/vo/Comment.java +++ b/backend/src/main/java/com/edufocus/edufocus/board/entity/vo/Comment.java @@ -10,7 +10,7 @@ import lombok.Builder; import lombok.NoArgsConstructor; import lombok.Setter; -import java.time.LocalTime; +import java.time.LocalDateTime; @Entity @Builder @@ -26,10 +26,10 @@ public class Comment { private String content; @Column - private LocalTime createdAt; + private LocalDateTime createdAt; @Column - private LocalTime modifiedAt; + private LocalDateTime modifiedAt; @ManyToOne @JoinColumn(name = "user_id") diff --git a/backend/src/main/resources/application.properties b/backend/src/main/resources/application.properties index f06afe6..e67341c 100644 --- a/backend/src/main/resources/application.properties +++ b/backend/src/main/resources/application.properties @@ -2,13 +2,15 @@ spring.application.name=edufocus server.port=8080 server.ssl.enabled=false +server.servlet.context-path=${CONTEXT_PATH} + # LiveKit configuration livekit.api.key=${LIVEKIT_API_KEY:devkey} livekit.api.secret=${LIVEKIT_API_SECRET:secret} # JWT Salt (??? ?? ???? ???) -jwt.salt=ssafy-screte-key-20240404-ssafy-screte-key-20240404-ssafy-screte-key-20240404 +jwt.salt=${SALT} # Access Token ?? ?? (??? ??) jwt.access-token.expiretime=3600000 @@ -16,11 +18,24 @@ jwt.access-token.expiretime=3600000 # Refresh Token ?? ?? (??? ??) jwt.refresh-token.expiretime=86400000 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver -spring.datasource.url=jdbc:mysql://localhost:3306/edufocus?useSSL=false -spring.datasource.username=root -spring.datasource.password=root +spring.datasource.url=${DATA_SOURCE_URL} +spring.datasource.username=${USER_NAME} +spring.datasource.password=${USER_PASSWORD} spring.mvc.pathmatch.matching-strategy=ant_path_matcher spring.jpa.database=mysql spring.jpa.hibernate.ddl-auto=create -spring.jpa.show-sql=true \ No newline at end of file +spring.jpa.show-sql=true + + + + +spring.mail.host=smtp.gmail.com +spring.mail.port=587 +#spring.mail.username=ssafytestpjt +#spring.mail.password=trpjbxqialufuzih +spring.mail.username=passfinder111@gmail.com +spring.mail.password=mnlyfkiprltjlsmw + +spring.mail.properties.mail.smtp.auth=true +spring.mail.properties.mail.smtp.starttls.enable=true \ No newline at end of file From 2d88ceed347104d8f4e7af6d019de2659ea03423 Mon Sep 17 00:00:00 2001 From: kgc9007 Date: Mon, 29 Jul 2024 10:55:07 +0900 Subject: [PATCH 15/32] =?UTF-8?q?fix:=20lecture=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?=EA=B2=B0=EA=B3=BC=20=EB=B0=98=ED=99=98=EA=B0=92=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lecture/controller/LectureController.java | 27 +++++++------------ .../lecture/service/LectureServiceImpl.java | 1 + 2 files changed, 10 insertions(+), 18 deletions(-) diff --git a/backend/src/main/java/com/edufocus/edufocus/lecture/controller/LectureController.java b/backend/src/main/java/com/edufocus/edufocus/lecture/controller/LectureController.java index b1895d5..e97cb07 100644 --- a/backend/src/main/java/com/edufocus/edufocus/lecture/controller/LectureController.java +++ b/backend/src/main/java/com/edufocus/edufocus/lecture/controller/LectureController.java @@ -35,14 +35,12 @@ public class LectureController { Lecture lecture = lectureService.findLectureByTitle(lectureCreateRequest.getTitle()); if (lecture != null) { - String msg = new String("Duplicated Lecture"); - return new ResponseEntity<>(msg, HttpStatus.CONFLICT); + return new ResponseEntity<>(HttpStatus.CONFLICT); } lectureService.createLecture(userId, lectureCreateRequest, image); - String msg = new String("Lecture registered successfully"); - return new ResponseEntity<>(msg, HttpStatus.CREATED); + return new ResponseEntity<>(HttpStatus.CREATED); } @PutMapping("/{lectureId}") @@ -50,11 +48,9 @@ public class LectureController { Long userId = Long.parseLong(jwtUtil.getUserId(accessToken)); if (!lectureService.updateLecture(userId, lectureId, lectureCreateRequest)) { - String msg = new String("Can't update Lecture"); - return new ResponseEntity<>(msg, HttpStatus.UNAUTHORIZED); + return new ResponseEntity<>(HttpStatus.UNAUTHORIZED); } - String msg = new String("Lecture updated successfully"); - return new ResponseEntity<>(msg, HttpStatus.OK); + return new ResponseEntity<>(HttpStatus.OK); } @@ -63,8 +59,7 @@ public class LectureController { Long userId = Long.parseLong(jwtUtil.getUserId(accessToken)); if (!lectureService.deleteLecture(userId, lectureId)) { - String msg = new String("Can't delete Lecture"); - return new ResponseEntity<>(msg, HttpStatus.UNAUTHORIZED); + return new ResponseEntity<>(HttpStatus.UNAUTHORIZED); } return new ResponseEntity<>(HttpStatus.NO_CONTENT); @@ -75,8 +70,7 @@ public class LectureController { List lectures = lectureService.findAllLecture(); if (lectures.isEmpty()) { - String msg = new String("No lectures found"); - return new ResponseEntity<>(msg, HttpStatus.OK); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); } return new ResponseEntity<>(lectures, HttpStatus.OK); @@ -93,8 +87,7 @@ public class LectureController { LectureDetailResponse lectureDetailResponse = lectureService.findLectureById(userId, lectureId); if (lectureDetailResponse == null) { - String msg = new String("Can't find Lecture"); - return new ResponseEntity<>(msg, HttpStatus.OK); + return new ResponseEntity<>(HttpStatus.OK); } @@ -104,8 +97,7 @@ public class LectureController { @GetMapping("/mylecture") public ResponseEntity findMyLecture(@RequestHeader(value = "Authorization", required = false) String accessToken) { if (accessToken == null) { - String msg = new String("Not logged in"); - return new ResponseEntity<>(msg, HttpStatus.OK); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); } Long userId = Long.parseLong(jwtUtil.getUserId(accessToken)); @@ -113,8 +105,7 @@ public class LectureController { List myLectures = lectureService.findMyLecture(userId); if (myLectures.isEmpty()) { - String msg = new String("No lectures found"); - return new ResponseEntity<>(msg, HttpStatus.OK); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); } return new ResponseEntity<>(myLectures, HttpStatus.OK); diff --git a/backend/src/main/java/com/edufocus/edufocus/lecture/service/LectureServiceImpl.java b/backend/src/main/java/com/edufocus/edufocus/lecture/service/LectureServiceImpl.java index 52a8824..e644018 100644 --- a/backend/src/main/java/com/edufocus/edufocus/lecture/service/LectureServiceImpl.java +++ b/backend/src/main/java/com/edufocus/edufocus/lecture/service/LectureServiceImpl.java @@ -119,6 +119,7 @@ public class LectureServiceImpl implements LectureService { @Override public List findAllLecture() { List lectureList = lectureRepository.findAll(); + System.out.println(lectureList.size()); List lectureSearchResponseList = new ArrayList<>(); for (Lecture lecture : lectureList) { From dc9a6ac3b846748da3ee7138546f7fb5657e4ce2 Mon Sep 17 00:00:00 2001 From: kgc9007 Date: Mon, 29 Jul 2024 11:35:21 +0900 Subject: [PATCH 16/32] =?UTF-8?q?fix:=20Lecture=20=EB=B9=88=20=EB=AA=A9?= =?UTF-8?q?=EB=A1=9D=20=EC=A1=B0=ED=9A=8C=EC=8B=9C=20=EB=B0=98=ED=99=98=20?= =?UTF-8?q?=ED=83=80=EC=9E=85=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lecture/controller/LectureController.java | 17 ----------------- .../lecture/service/LectureServiceImpl.java | 8 +------- 2 files changed, 1 insertion(+), 24 deletions(-) diff --git a/backend/src/main/java/com/edufocus/edufocus/lecture/controller/LectureController.java b/backend/src/main/java/com/edufocus/edufocus/lecture/controller/LectureController.java index e97cb07..6e04639 100644 --- a/backend/src/main/java/com/edufocus/edufocus/lecture/controller/LectureController.java +++ b/backend/src/main/java/com/edufocus/edufocus/lecture/controller/LectureController.java @@ -14,10 +14,7 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; -import java.io.File; -import java.io.IOException; import java.util.List; -import java.util.UUID; @RestController @RequestMapping("/lecture") @@ -69,10 +66,6 @@ public class LectureController { public ResponseEntity findAllLecture() { List lectures = lectureService.findAllLecture(); - if (lectures.isEmpty()) { - return new ResponseEntity<>(HttpStatus.NO_CONTENT); - } - return new ResponseEntity<>(lectures, HttpStatus.OK); } @@ -83,14 +76,8 @@ public class LectureController { if (accessToken != null) { userId = Long.parseLong(jwtUtil.getUserId(accessToken)); } - LectureDetailResponse lectureDetailResponse = lectureService.findLectureById(userId, lectureId); - if (lectureDetailResponse == null) { - return new ResponseEntity<>(HttpStatus.OK); - } - - return new ResponseEntity<>(lectureDetailResponse, HttpStatus.OK); } @@ -104,10 +91,6 @@ public class LectureController { List myLectures = lectureService.findMyLecture(userId); - if (myLectures.isEmpty()) { - return new ResponseEntity<>(HttpStatus.NO_CONTENT); - } - return new ResponseEntity<>(myLectures, HttpStatus.OK); } } diff --git a/backend/src/main/java/com/edufocus/edufocus/lecture/service/LectureServiceImpl.java b/backend/src/main/java/com/edufocus/edufocus/lecture/service/LectureServiceImpl.java index e644018..babbea5 100644 --- a/backend/src/main/java/com/edufocus/edufocus/lecture/service/LectureServiceImpl.java +++ b/backend/src/main/java/com/edufocus/edufocus/lecture/service/LectureServiceImpl.java @@ -18,10 +18,7 @@ import org.springframework.web.multipart.MultipartFile; import java.io.File; import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import java.util.UUID; +import java.util.*; @Builder @Service @@ -119,7 +116,6 @@ public class LectureServiceImpl implements LectureService { @Override public List findAllLecture() { List lectureList = lectureRepository.findAll(); - System.out.println(lectureList.size()); List lectureSearchResponseList = new ArrayList<>(); for (Lecture lecture : lectureList) { @@ -137,11 +133,9 @@ public class LectureServiceImpl implements LectureService { @Override public LectureDetailResponse findLectureById(Long userId, long lectureId) { Optional lecture = lectureRepository.findById(lectureId); - if (lecture.isEmpty()) { return null; } - lecture = Optional.of(lecture.get()); String userStatus; if (userId == null) { From 32a176b679c5acd02e0439897ce9aa458600a519 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A0=95=EB=AF=BC?= Date: Mon, 29 Jul 2024 14:08:44 +0900 Subject: [PATCH 17/32] =?UTF-8?q?feat:=20=EA=B0=95=EC=9D=98=20=EC=8B=9C?= =?UTF-8?q?=EC=9E=91,=20=EA=B0=95=EC=9D=98=20=EC=B0=B8=EC=97=AC=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../edufocus/lecture/entity/Lecture.java | 2 +- .../lecture/service/LectureService.java | 2 + .../lecture/service/LectureServiceImpl.java | 24 +++ .../service/RegistrationService.java | 2 + .../service/RegistrationServiceImpl.java | 7 + .../user/config/WebConfiguration.java | 4 +- .../edufocus/video/controller/Controller.java | 150 +++++++++++++----- .../edufocus/video/service/VideoSertvice.java | 8 + .../video/service/VideoServiceImpl.java | 21 +++ .../src/main/resources/application.properties | 3 + 10 files changed, 179 insertions(+), 44 deletions(-) create mode 100644 backend/src/main/java/com/edufocus/edufocus/video/service/VideoSertvice.java create mode 100644 backend/src/main/java/com/edufocus/edufocus/video/service/VideoServiceImpl.java diff --git a/backend/src/main/java/com/edufocus/edufocus/lecture/entity/Lecture.java b/backend/src/main/java/com/edufocus/edufocus/lecture/entity/Lecture.java index 0a9168a..f74d97f 100644 --- a/backend/src/main/java/com/edufocus/edufocus/lecture/entity/Lecture.java +++ b/backend/src/main/java/com/edufocus/edufocus/lecture/entity/Lecture.java @@ -47,7 +47,7 @@ public class Lecture { @Column private String time; - @Column + @Column(columnDefinition = "boolean default false") private boolean online; } diff --git a/backend/src/main/java/com/edufocus/edufocus/lecture/service/LectureService.java b/backend/src/main/java/com/edufocus/edufocus/lecture/service/LectureService.java index 962ed5d..65153a0 100644 --- a/backend/src/main/java/com/edufocus/edufocus/lecture/service/LectureService.java +++ b/backend/src/main/java/com/edufocus/edufocus/lecture/service/LectureService.java @@ -26,4 +26,6 @@ public interface LectureService { List findMyLecture(long userId); Lecture findLectureByTitle(String title); + + void changeState(Long lectureId); } diff --git a/backend/src/main/java/com/edufocus/edufocus/lecture/service/LectureServiceImpl.java b/backend/src/main/java/com/edufocus/edufocus/lecture/service/LectureServiceImpl.java index 52a8824..0f841ad 100644 --- a/backend/src/main/java/com/edufocus/edufocus/lecture/service/LectureServiceImpl.java +++ b/backend/src/main/java/com/edufocus/edufocus/lecture/service/LectureServiceImpl.java @@ -221,4 +221,28 @@ public class LectureServiceImpl implements LectureService { public Lecture findLectureByTitle(String title) { return lectureRepository.findByTitle(title); } + + @Override + public void changeState(Long id) { + + Optional lecture = lectureRepository.findById(id); + + Lecture l; + if(lecture.isPresent()) + { + l = lecture.get(); + + System.out.println(l.isOnline()); + l.setOnline(true); + System.out.println(l.isOnline()); + + + }else { + + throw new RuntimeException("Lecture not found with id: " + id); + } + lectureRepository.save(l); + } + + } diff --git a/backend/src/main/java/com/edufocus/edufocus/registration/service/RegistrationService.java b/backend/src/main/java/com/edufocus/edufocus/registration/service/RegistrationService.java index 96d89f6..bc27b37 100644 --- a/backend/src/main/java/com/edufocus/edufocus/registration/service/RegistrationService.java +++ b/backend/src/main/java/com/edufocus/edufocus/registration/service/RegistrationService.java @@ -1,6 +1,7 @@ package com.edufocus.edufocus.registration.service; import com.edufocus.edufocus.registration.entity.Registration; +import com.edufocus.edufocus.registration.entity.RegistrationStatus; import org.springframework.stereotype.Service; @Service @@ -12,4 +13,5 @@ public interface RegistrationService { boolean deleteRegistration(long userId, long registrationId); + RegistrationStatus isOnline(Long userId , Long lectureId); } diff --git a/backend/src/main/java/com/edufocus/edufocus/registration/service/RegistrationServiceImpl.java b/backend/src/main/java/com/edufocus/edufocus/registration/service/RegistrationServiceImpl.java index 7c156bf..0eca5b5 100644 --- a/backend/src/main/java/com/edufocus/edufocus/registration/service/RegistrationServiceImpl.java +++ b/backend/src/main/java/com/edufocus/edufocus/registration/service/RegistrationServiceImpl.java @@ -62,4 +62,11 @@ public class RegistrationServiceImpl implements RegistrationService { return true; } + @Override + public RegistrationStatus isOnline(Long userId, Long lectureId) { + + Registration registration = registrationRepository.findByUserIdAndLectureId(userId,lectureId); + return registration.getStatus(); + } + } diff --git a/backend/src/main/java/com/edufocus/edufocus/user/config/WebConfiguration.java b/backend/src/main/java/com/edufocus/edufocus/user/config/WebConfiguration.java index 4bc3d83..58213a3 100644 --- a/backend/src/main/java/com/edufocus/edufocus/user/config/WebConfiguration.java +++ b/backend/src/main/java/com/edufocus/edufocus/user/config/WebConfiguration.java @@ -26,7 +26,7 @@ public class WebConfiguration implements WebMvcConfigurer { public void addCorsMappings(CorsRegistry registry) { registry .addMapping("/**") - .allowedOrigins("http://i11a701.p.ssafy.io/", "http://localhost:5173", "http://localhost:4173") + .allowedOrigins("http://i11a701.p.ssafy.io/", "http://localhost:5173", "http://localhost:4173","http://localhost:5080") .allowedMethods(HttpMethod.GET.name(), HttpMethod.POST.name(), HttpMethod.PUT.name(), HttpMethod.DELETE.name(), HttpMethod.HEAD.name(), HttpMethod.OPTIONS.name(), HttpMethod.PATCH.name()) @@ -45,7 +45,7 @@ public class WebConfiguration implements WebMvcConfigurer { registry.addInterceptor(jwtInterceptor) .addPathPatterns("/**") // 모든 경로에 대해 인터셉터 적용 - .excludePathPatterns("/v3/api-docs/**","/swagger-resources/**","/webjars/**","/swagger-ui/**","/auth/**", "/board/**", "/user/**","/lecture/**","/qna/**", "/quiz/**"); // 인증 없이 접근 가능한 경로 설정 + .excludePathPatterns("/v3/api-docs/**","/swagger-resources/**","/webjars/**","/swagger-ui/**","/auth/**", "/board/**", "/user/**","/lecture/**","/qna/**", "/quiz/**","/video/**"); // 인증 없이 접근 가능한 경로 설정 ///v3/api-docs/**, /swagger-resources/**, /webjars/** } diff --git a/backend/src/main/java/com/edufocus/edufocus/video/controller/Controller.java b/backend/src/main/java/com/edufocus/edufocus/video/controller/Controller.java index ed35745..36cadb8 100644 --- a/backend/src/main/java/com/edufocus/edufocus/video/controller/Controller.java +++ b/backend/src/main/java/com/edufocus/edufocus/video/controller/Controller.java @@ -1,20 +1,37 @@ package com.edufocus.edufocus.video.controller; -import io.livekit.server.AccessToken; -import io.livekit.server.RoomJoin; -import io.livekit.server.RoomName; -import io.livekit.server.WebhookReceiver; -import livekit.LivekitWebhook.WebhookEvent; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; - import java.util.Map; -@CrossOrigin(origins = "*") -@RestController -public class Controller { +import com.edufocus.edufocus.lecture.entity.Lecture; +import com.edufocus.edufocus.lecture.entity.LectureDetailResponse; +import com.edufocus.edufocus.lecture.service.LectureService; +import com.edufocus.edufocus.registration.entity.RegistrationStatus; +import com.edufocus.edufocus.registration.service.RegistrationService; +import com.edufocus.edufocus.user.model.service.UserService; +import com.edufocus.edufocus.user.util.JWTUtil; +import com.edufocus.edufocus.video.service.VideoSertvice; +import io.livekit.server.*; +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import livekit.LivekitWebhook.WebhookEvent; + +//@CrossOrigin(origins = "*") +@RestController +@RequestMapping("/video") +@Slf4j +@RequiredArgsConstructor +public class Controller { + private final JWTUtil jwtUtil; + private final UserService userService; + private final LectureService lectureService; + private final VideoSertvice videoSertvice; + private final RegistrationService registrationService; @Value("${livekit.api.key}") private String LIVEKIT_API_KEY; @@ -25,47 +42,98 @@ public class Controller { * @param params JSON object with roomName and participantName * @return JSON object with the JWT token */ - @PostMapping(value = "/token") - public ResponseEntity> createToken(@RequestBody Map params) { - String roomName = params.get("roomName"); - String participantName = params.get("participantName"); +// @PostMapping(value = "/token") +// public ResponseEntity> createToken(@RequestBody Map params) { +// String roomName = params.get("roomName"); +// String participantName = params.get("participantName"); +// +// if (roomName == null || participantName == null) { +// return ResponseEntity.badRequest().body(Map.of("errorMessage", "roomName and participantName are required")); +// } +// +// AccessToken token = new AccessToken(LIVEKIT_API_KEY, LIVEKIT_API_SECRET); +// token.setName(participantName); +// token.setIdentity(participantName); +// token.addGrants(new RoomJoin(true), new RoomName(roomName)); +// +// +// +// return ResponseEntity.ok(Map.of("token", token.toJwt())); +// } - // - // - // - // - // - // + @PostMapping(value = "/makeroom/{lecture_id}") + public ResponseEntity> startLecture(@PathVariable("lecture_id") Long id, HttpServletRequest request) throws Exception { + + String userToken = request.getHeader("Authorization"); + + Long userId = Long.parseLong(jwtUtil.getUserId(userToken)); + LectureDetailResponse lecture= lectureService.findLectureById(userId,id); + + String roomName = lecture.getTitle(); + String participantName = userService.getUserName(userId); - if (roomName == null || participantName == null) { - return ResponseEntity.badRequest().body(Map.of("errorMessage", "roomName and participantName are required")); - } AccessToken token = new AccessToken(LIVEKIT_API_KEY, LIVEKIT_API_SECRET); - - token.setName(participantName); token.setIdentity(participantName); - token.addGrants(new RoomJoin(true), new RoomName(roomName)); + token.addGrants(new RoomJoin(true), new RoomName(roomName),new RoomCreate(true)); + + videoSertvice.startOnline(userId,id); + - // 방이름으로 입장하는건데 - // 만약 수강생에 DB에 신청한 강의명이 없다면 들어갈수 없음 - // 검증 로직만 추가하면 될듯? return ResponseEntity.ok(Map.of("token", token.toJwt())); } - @PostMapping(value = "/webhook", consumes = "application/webhook+json") - public ResponseEntity receiveWebhook(@RequestHeader("Authorization") String authHeader, @RequestBody String body) { - WebhookReceiver webhookReceiver = new WebhookReceiver(LIVEKIT_API_KEY, LIVEKIT_API_SECRET); - try { - WebhookEvent event = webhookReceiver.receive(body, authHeader); - System.out.println("LiveKit Webhook: " + event.toString()); - } catch (Exception e) { - System.err.println("Error validating webhook event: " + e.getMessage()); - } - return ResponseEntity.ok("ok"); + + @PostMapping(value = "/joinroom/{lecture_id}") + public ResponseEntity> joinRoom(@PathVariable("lecture_id") Long id, HttpServletRequest request) throws Exception { + + String userToken = request.getHeader("Authorization"); + + Long userId = Long.parseLong(jwtUtil.getUserId(userToken)); + LectureDetailResponse lecture= lectureService.findLectureById(userId,id); + + + RegistrationStatus registrationStatus = registrationService.isOnline(userId,id); + + if(registrationStatus==RegistrationStatus.ACCEPTED) + { + String roomName = lecture.getTitle(); + String participantName = userService.getUserName(userId); + + + AccessToken token = new AccessToken(LIVEKIT_API_KEY, LIVEKIT_API_SECRET); + token.setName(participantName); + token.setIdentity(participantName); + token.addGrants(new RoomJoin(true), new RoomName(roomName)); + + videoSertvice.startOnline(userId,id); + + + + return ResponseEntity.ok(Map.of("token", token.toJwt())); + } + else{ + return ResponseEntity.status(HttpStatus.FORBIDDEN).body(Map.of("errorMessage", "Not accepted")); + + } + + + + } + + @PostMapping(value = "/livekit/webhook", consumes = "application/webhook+json") + public ResponseEntity receiveWebhook(@RequestHeader("Authorization") String authHeader, @RequestBody String body) { + WebhookReceiver webhookReceiver = new WebhookReceiver(LIVEKIT_API_KEY, LIVEKIT_API_SECRET); + try { + WebhookEvent event = webhookReceiver.receive(body, authHeader); + System.out.println("LiveKit Webhook: " + event.toString()); + } catch (Exception e) { + System.err.println("Error validating webhook event: " + e.getMessage()); + } + return ResponseEntity.ok("ok"); } } diff --git a/backend/src/main/java/com/edufocus/edufocus/video/service/VideoSertvice.java b/backend/src/main/java/com/edufocus/edufocus/video/service/VideoSertvice.java new file mode 100644 index 0000000..981c5c4 --- /dev/null +++ b/backend/src/main/java/com/edufocus/edufocus/video/service/VideoSertvice.java @@ -0,0 +1,8 @@ +package com.edufocus.edufocus.video.service; + +import java.sql.SQLException; + +public interface VideoSertvice { + + void startOnline(Long userId,Long lectureId) throws SQLException; +} diff --git a/backend/src/main/java/com/edufocus/edufocus/video/service/VideoServiceImpl.java b/backend/src/main/java/com/edufocus/edufocus/video/service/VideoServiceImpl.java new file mode 100644 index 0000000..af82559 --- /dev/null +++ b/backend/src/main/java/com/edufocus/edufocus/video/service/VideoServiceImpl.java @@ -0,0 +1,21 @@ +package com.edufocus.edufocus.video.service; + +import com.edufocus.edufocus.lecture.entity.Lecture; +import com.edufocus.edufocus.lecture.service.LectureService; +import jakarta.transaction.Transactional; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.sql.SQLException; +@Service +@Transactional +@RequiredArgsConstructor +public class VideoServiceImpl implements VideoSertvice{ + + private final LectureService lectureService; + @Override + public void startOnline(Long userId,Long lectureId) throws SQLException { + + lectureService.changeState(lectureId); + } +} diff --git a/backend/src/main/resources/application.properties b/backend/src/main/resources/application.properties index 37b4488..b188226 100644 --- a/backend/src/main/resources/application.properties +++ b/backend/src/main/resources/application.properties @@ -4,6 +4,9 @@ server.port=8080 server.ssl.enabled=false server.servlet.context-path=${CONTEXT_PATH} +# openvidu +#server.port=${SERVER_PORT:6080} + # LiveKit configuration livekit.api.key=${LIVEKIT_API_KEY:devkey} From 16fd51641a126b1613be0850aef00da29ef533d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A0=95=EB=AF=BC?= Date: Mon, 29 Jul 2024 14:33:27 +0900 Subject: [PATCH 18/32] =?UTF-8?q?feat:=20regisration=20cors=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/edufocus/edufocus/user/config/WebConfiguration.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/main/java/com/edufocus/edufocus/user/config/WebConfiguration.java b/backend/src/main/java/com/edufocus/edufocus/user/config/WebConfiguration.java index 58213a3..9b421ef 100644 --- a/backend/src/main/java/com/edufocus/edufocus/user/config/WebConfiguration.java +++ b/backend/src/main/java/com/edufocus/edufocus/user/config/WebConfiguration.java @@ -45,7 +45,7 @@ public class WebConfiguration implements WebMvcConfigurer { registry.addInterceptor(jwtInterceptor) .addPathPatterns("/**") // 모든 경로에 대해 인터셉터 적용 - .excludePathPatterns("/v3/api-docs/**","/swagger-resources/**","/webjars/**","/swagger-ui/**","/auth/**", "/board/**", "/user/**","/lecture/**","/qna/**", "/quiz/**","/video/**"); // 인증 없이 접근 가능한 경로 설정 + .excludePathPatterns("/v3/api-docs/**","/swagger-resources/**","/webjars/**","/swagger-ui/**","/auth/**", "/board/**", "/user/**","/lecture/**","/qna/**", "/quiz/**","/video/**","/registration/**"); // 인증 없이 접근 가능한 경로 설정 ///v3/api-docs/**, /swagger-resources/**, /webjars/** } From 32d2592841c32eb65e9259383aa4232c803bfb06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A0=95=EB=AF=BC?= Date: Mon, 29 Jul 2024 14:58:44 +0900 Subject: [PATCH 19/32] feat: cors --- .../edufocus/edufocus/user/model/entity/InfoDto.java | 8 ++++++++ .../edufocus/user/model/service/UserService.java | 3 +++ .../edufocus/user/model/service/UserServiceImpl.java | 10 ++++++++++ .../edufocus/edufocus/video/controller/Controller.java | 3 ++- 4 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 backend/src/main/java/com/edufocus/edufocus/user/model/entity/InfoDto.java diff --git a/backend/src/main/java/com/edufocus/edufocus/user/model/entity/InfoDto.java b/backend/src/main/java/com/edufocus/edufocus/user/model/entity/InfoDto.java new file mode 100644 index 0000000..ed96b9c --- /dev/null +++ b/backend/src/main/java/com/edufocus/edufocus/user/model/entity/InfoDto.java @@ -0,0 +1,8 @@ +package com.edufocus.edufocus.user.model.entity; + +public class InfoDto { + + String name; + String email; + +} diff --git a/backend/src/main/java/com/edufocus/edufocus/user/model/service/UserService.java b/backend/src/main/java/com/edufocus/edufocus/user/model/service/UserService.java index f661fd1..2f63135 100644 --- a/backend/src/main/java/com/edufocus/edufocus/user/model/service/UserService.java +++ b/backend/src/main/java/com/edufocus/edufocus/user/model/service/UserService.java @@ -12,4 +12,7 @@ public interface UserService { void sendEamail(User user) throws Exception; void userCheck(Long id) throws Exception; String getUserName(Long id) throws Exception; + void changeuInfo(Long id) throws Exception; + void changePassword(Long id) throws Exception; + } diff --git a/backend/src/main/java/com/edufocus/edufocus/user/model/service/UserServiceImpl.java b/backend/src/main/java/com/edufocus/edufocus/user/model/service/UserServiceImpl.java index 554d971..66bb873 100644 --- a/backend/src/main/java/com/edufocus/edufocus/user/model/service/UserServiceImpl.java +++ b/backend/src/main/java/com/edufocus/edufocus/user/model/service/UserServiceImpl.java @@ -143,6 +143,16 @@ public class UserServiceImpl implements UserService{ return userRepository.findById(id).get().getName(); } + @Override + public void changeuInfo(Long id) throws Exception { + + } + + @Override + public void changePassword(Long id) throws Exception { + + } + public String getTempPassword() { char[] charSet = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'}; diff --git a/backend/src/main/java/com/edufocus/edufocus/video/controller/Controller.java b/backend/src/main/java/com/edufocus/edufocus/video/controller/Controller.java index 36cadb8..b6c1493 100644 --- a/backend/src/main/java/com/edufocus/edufocus/video/controller/Controller.java +++ b/backend/src/main/java/com/edufocus/edufocus/video/controller/Controller.java @@ -21,11 +21,12 @@ import org.springframework.web.bind.annotation.*; import livekit.LivekitWebhook.WebhookEvent; -//@CrossOrigin(origins = "*") +@CrossOrigin(origins = "*") @RestController @RequestMapping("/video") @Slf4j @RequiredArgsConstructor + public class Controller { private final JWTUtil jwtUtil; private final UserService userService; From bcf36665f9eeaa85e8c40c5a34139f05aab1d42d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A0=95=EB=AF=BC?= Date: Mon, 29 Jul 2024 15:11:25 +0900 Subject: [PATCH 20/32] =?UTF-8?q?feat:=20cors=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/edufocus/edufocus/video/controller/Controller.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/backend/src/main/java/com/edufocus/edufocus/video/controller/Controller.java b/backend/src/main/java/com/edufocus/edufocus/video/controller/Controller.java index b6c1493..b36b4d2 100644 --- a/backend/src/main/java/com/edufocus/edufocus/video/controller/Controller.java +++ b/backend/src/main/java/com/edufocus/edufocus/video/controller/Controller.java @@ -21,7 +21,7 @@ import org.springframework.web.bind.annotation.*; import livekit.LivekitWebhook.WebhookEvent; -@CrossOrigin(origins = "*") + @RestController @RequestMapping("/video") @Slf4j @@ -66,6 +66,8 @@ public class Controller { public ResponseEntity> startLecture(@PathVariable("lecture_id") Long id, HttpServletRequest request) throws Exception { String userToken = request.getHeader("Authorization"); + log.info("userToekn : ", userToken); + Long userId = Long.parseLong(jwtUtil.getUserId(userToken)); LectureDetailResponse lecture= lectureService.findLectureById(userId,id); From 764f5bd30db768bf0818a6daf3c89df0d122f763 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A0=95=EB=AF=BC?= Date: Mon, 29 Jul 2024 15:18:37 +0900 Subject: [PATCH 21/32] feat: video --- .../edufocus/video/controller/Controller.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/backend/src/main/java/com/edufocus/edufocus/video/controller/Controller.java b/backend/src/main/java/com/edufocus/edufocus/video/controller/Controller.java index b36b4d2..6ad9ef3 100644 --- a/backend/src/main/java/com/edufocus/edufocus/video/controller/Controller.java +++ b/backend/src/main/java/com/edufocus/edufocus/video/controller/Controller.java @@ -99,10 +99,10 @@ public class Controller { LectureDetailResponse lecture= lectureService.findLectureById(userId,id); - RegistrationStatus registrationStatus = registrationService.isOnline(userId,id); + //RegistrationStatus registrationStatus = registrationService.isOnline(userId,id); - if(registrationStatus==RegistrationStatus.ACCEPTED) - { +// if(registrationStatus==RegistrationStatus.ACCEPTED) +// { String roomName = lecture.getTitle(); String participantName = userService.getUserName(userId); @@ -112,16 +112,16 @@ public class Controller { token.setIdentity(participantName); token.addGrants(new RoomJoin(true), new RoomName(roomName)); - videoSertvice.startOnline(userId,id); + //videoSertvice.startOnline(userId,id); return ResponseEntity.ok(Map.of("token", token.toJwt())); - } - else{ - return ResponseEntity.status(HttpStatus.FORBIDDEN).body(Map.of("errorMessage", "Not accepted")); - - } +// } +// else{ +// return ResponseEntity.status(HttpStatus.FORBIDDEN).body(Map.of("errorMessage", "Not accepted")); +// +// } From 8dd5676a265c5c7aba2ec24c446181defed77cbd Mon Sep 17 00:00:00 2001 From: kgc9007 Date: Mon, 29 Jul 2024 17:54:27 +0900 Subject: [PATCH 22/32] =?UTF-8?q?feat:=20Quiz=20=EB=93=B1=EB=A1=9D,=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20=EC=9A=94=EC=B2=AD=20=ED=98=95=EC=8B=9D=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../quiz/controller/QuizController.java | 24 ++---------- .../edufocus/edufocus/quiz/entity/Choice.java | 29 ++++++++++++++ ...eRequest.java => ChoiceCreateRequest.java} | 7 ++-- .../edufocus/edufocus/quiz/entity/Quiz.java | 35 +++++++---------- .../quiz/entity/QuizCreateRequest.java | 16 ++------ .../edufocus/quiz/entity/QuizResponse.java | 21 ++++++++++ .../quiz/entity/QuizSetCreateRequest.java | 4 -- .../edufocus/quiz/entity/QuizSetResponse.java | 20 ++++++++++ .../edufocus/quiz/entity/QuizType.java | 5 --- .../quiz/repository/ChoiceRepository.java | 8 ++++ .../quiz/repository/QuizRepository.java | 1 - .../edufocus/quiz/service/ChoiceService.java | 7 ++++ .../quiz/service/ChoiceServiceImpl.java | 25 ++++++++++++ .../edufocus/quiz/service/QuizService.java | 4 +- .../quiz/service/QuizServiceImpl.java | 39 +++++++++++-------- .../edufocus/quiz/service/QuizSetService.java | 7 +++- .../quiz/service/QuizSetServiceImpl.java | 36 ++++++++++++++--- .../controller/RegistrationController.java | 4 +- 18 files changed, 196 insertions(+), 96 deletions(-) create mode 100644 backend/src/main/java/com/edufocus/edufocus/quiz/entity/Choice.java rename backend/src/main/java/com/edufocus/edufocus/quiz/entity/{SetCreateRequest.java => ChoiceCreateRequest.java} (68%) create mode 100644 backend/src/main/java/com/edufocus/edufocus/quiz/entity/QuizResponse.java create mode 100644 backend/src/main/java/com/edufocus/edufocus/quiz/entity/QuizSetResponse.java delete mode 100644 backend/src/main/java/com/edufocus/edufocus/quiz/entity/QuizType.java create mode 100644 backend/src/main/java/com/edufocus/edufocus/quiz/repository/ChoiceRepository.java create mode 100644 backend/src/main/java/com/edufocus/edufocus/quiz/service/ChoiceService.java create mode 100644 backend/src/main/java/com/edufocus/edufocus/quiz/service/ChoiceServiceImpl.java diff --git a/backend/src/main/java/com/edufocus/edufocus/quiz/controller/QuizController.java b/backend/src/main/java/com/edufocus/edufocus/quiz/controller/QuizController.java index fc3abcd..ac58dd2 100644 --- a/backend/src/main/java/com/edufocus/edufocus/quiz/controller/QuizController.java +++ b/backend/src/main/java/com/edufocus/edufocus/quiz/controller/QuizController.java @@ -33,28 +33,10 @@ public class QuizController { , @RequestPart(value = "image", required = false) MultipartFile image) throws IOException { Long userId = Long.parseLong(jwtUtil.getUserId(accessToken)); - String title = quizSetCreateRequest.getTitle(); - SetCreateRequest setCreateRequest = new SetCreateRequest(userId, title); - - QuizSet quizSet = quizSetService.createQuizSet(setCreateRequest); - if (image != null && !image.isEmpty()) { - String uid = UUID.randomUUID().toString(); - - String currentPath = "backend/src/main/resources/images/"; - File checkPathFile = new File(currentPath); - if (!checkPathFile.exists()) { - checkPathFile.mkdirs(); - } - - File savingImage = new File(currentPath + uid + "_" + image.getOriginalFilename()); - image.transferTo(savingImage.toPath()); - String savePath = savingImage.toPath().toString(); - - quizSet.setImage(savePath); - } + QuizSet quizSet = quizSetService.createQuizSet(userId, quizSetCreateRequest.getTitle()); for (QuizCreateRequest quizCreateRequest : quizSetCreateRequest.getQuizzes()) { - quizService.createQuiz(quizSet.getId(), quizCreateRequest); + quizService.createQuiz(quizSet, quizCreateRequest); } return new ResponseEntity<>(HttpStatus.CREATED); @@ -62,7 +44,7 @@ public class QuizController { @GetMapping("/{quizsetId}") public ResponseEntity getQuizzes(@PathVariable Long quizsetId) { - QuizSet quizSet = quizSetService.findQuizSet(quizsetId); + QuizSetResponse quizSet = quizSetService.findQuizSetResponse(quizsetId); return new ResponseEntity<>(quizSet, HttpStatus.OK); } diff --git a/backend/src/main/java/com/edufocus/edufocus/quiz/entity/Choice.java b/backend/src/main/java/com/edufocus/edufocus/quiz/entity/Choice.java new file mode 100644 index 0000000..d31f8a1 --- /dev/null +++ b/backend/src/main/java/com/edufocus/edufocus/quiz/entity/Choice.java @@ -0,0 +1,29 @@ +package com.edufocus.edufocus.quiz.entity; + +import com.fasterxml.jackson.annotation.JsonBackReference; +import jakarta.persistence.*; +import lombok.*; + +@Entity +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class Choice { + + @Id + @GeneratedValue + private Long id; + + @ManyToOne + @JoinColumn(name = "QuizId") + @JsonBackReference + private Quiz quiz; + + @Column + private int num; + + @Column + private String content; +} diff --git a/backend/src/main/java/com/edufocus/edufocus/quiz/entity/SetCreateRequest.java b/backend/src/main/java/com/edufocus/edufocus/quiz/entity/ChoiceCreateRequest.java similarity index 68% rename from backend/src/main/java/com/edufocus/edufocus/quiz/entity/SetCreateRequest.java rename to backend/src/main/java/com/edufocus/edufocus/quiz/entity/ChoiceCreateRequest.java index 83caff8..80f8fc4 100644 --- a/backend/src/main/java/com/edufocus/edufocus/quiz/entity/SetCreateRequest.java +++ b/backend/src/main/java/com/edufocus/edufocus/quiz/entity/ChoiceCreateRequest.java @@ -7,10 +7,9 @@ import lombok.NoArgsConstructor; @Getter @NoArgsConstructor @AllArgsConstructor -public class SetCreateRequest { +public class ChoiceCreateRequest { - private Long UserId; - - private String title; + private int num; + private String content; } diff --git a/backend/src/main/java/com/edufocus/edufocus/quiz/entity/Quiz.java b/backend/src/main/java/com/edufocus/edufocus/quiz/entity/Quiz.java index ad429ac..f9a3cc7 100644 --- a/backend/src/main/java/com/edufocus/edufocus/quiz/entity/Quiz.java +++ b/backend/src/main/java/com/edufocus/edufocus/quiz/entity/Quiz.java @@ -1,11 +1,12 @@ package com.edufocus.edufocus.quiz.entity; -import com.edufocus.edufocus.user.model.entity.UserRole; import com.fasterxml.jackson.annotation.JsonBackReference; import com.fasterxml.jackson.annotation.JsonManagedReference; import jakarta.persistence.*; import lombok.*; +import java.util.List; + @Entity @Getter @Setter @@ -24,31 +25,17 @@ public class Quiz { private QuizSet quizSet; @Column - private String title; - - @Column - private String description; - - @Column - private String answer; - - @Enumerated(EnumType.STRING) - private QuizType quizType; + private String question; @Column private String image; @Column - private String choice1; + private String answer; - @Column - private String choice2; - - @Column - private String choice3; - - @Column - private String choice4; + @OneToMany(mappedBy = "quiz") + @JsonManagedReference + private List choices; public void setQuizSet(QuizSet quizSet) { this.quizSet = quizSet; @@ -57,4 +44,12 @@ public class Quiz { quizSet.getQuizzes().remove(this); } } + + public void addChoice(Choice choice) { + this.choices.add(choice); + + if (choice.getQuiz() != this) { + choice.setQuiz(this); + } + } } diff --git a/backend/src/main/java/com/edufocus/edufocus/quiz/entity/QuizCreateRequest.java b/backend/src/main/java/com/edufocus/edufocus/quiz/entity/QuizCreateRequest.java index c140c79..9ac60bd 100644 --- a/backend/src/main/java/com/edufocus/edufocus/quiz/entity/QuizCreateRequest.java +++ b/backend/src/main/java/com/edufocus/edufocus/quiz/entity/QuizCreateRequest.java @@ -4,24 +4,16 @@ import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; +import java.util.List; + @Getter @NoArgsConstructor @AllArgsConstructor public class QuizCreateRequest { - private String title; - - private String description; + private String question; private String answer; - private String quizType; - - private String choice1; - - private String choice2; - - private String choice3; - - private String choice4; + private List choices; } diff --git a/backend/src/main/java/com/edufocus/edufocus/quiz/entity/QuizResponse.java b/backend/src/main/java/com/edufocus/edufocus/quiz/entity/QuizResponse.java new file mode 100644 index 0000000..0b0be9f --- /dev/null +++ b/backend/src/main/java/com/edufocus/edufocus/quiz/entity/QuizResponse.java @@ -0,0 +1,21 @@ +package com.edufocus.edufocus.quiz.entity; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class QuizResponse { + + private String question; + + private String image; + + private List choices; +} diff --git a/backend/src/main/java/com/edufocus/edufocus/quiz/entity/QuizSetCreateRequest.java b/backend/src/main/java/com/edufocus/edufocus/quiz/entity/QuizSetCreateRequest.java index 20a0025..0a137a3 100644 --- a/backend/src/main/java/com/edufocus/edufocus/quiz/entity/QuizSetCreateRequest.java +++ b/backend/src/main/java/com/edufocus/edufocus/quiz/entity/QuizSetCreateRequest.java @@ -11,12 +11,8 @@ import java.util.List; @AllArgsConstructor public class QuizSetCreateRequest { - private Long UserId; - private String title; - private String image; - private List quizzes; } \ No newline at end of file diff --git a/backend/src/main/java/com/edufocus/edufocus/quiz/entity/QuizSetResponse.java b/backend/src/main/java/com/edufocus/edufocus/quiz/entity/QuizSetResponse.java new file mode 100644 index 0000000..9fdaf5e --- /dev/null +++ b/backend/src/main/java/com/edufocus/edufocus/quiz/entity/QuizSetResponse.java @@ -0,0 +1,20 @@ +package com.edufocus.edufocus.quiz.entity; + +import lombok.*; + +import java.util.ArrayList; +import java.util.List; + +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class QuizSetResponse { + + private String title; + + private String image; + + private List quizzes; +} diff --git a/backend/src/main/java/com/edufocus/edufocus/quiz/entity/QuizType.java b/backend/src/main/java/com/edufocus/edufocus/quiz/entity/QuizType.java deleted file mode 100644 index b58f56c..0000000 --- a/backend/src/main/java/com/edufocus/edufocus/quiz/entity/QuizType.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.edufocus.edufocus.quiz.entity; - -public enum QuizType { - SINGLE, MULTIPLE -} diff --git a/backend/src/main/java/com/edufocus/edufocus/quiz/repository/ChoiceRepository.java b/backend/src/main/java/com/edufocus/edufocus/quiz/repository/ChoiceRepository.java new file mode 100644 index 0000000..740dda2 --- /dev/null +++ b/backend/src/main/java/com/edufocus/edufocus/quiz/repository/ChoiceRepository.java @@ -0,0 +1,8 @@ +package com.edufocus.edufocus.quiz.repository; + +import com.edufocus.edufocus.quiz.entity.Choice; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface ChoiceRepository extends JpaRepository { + +} diff --git a/backend/src/main/java/com/edufocus/edufocus/quiz/repository/QuizRepository.java b/backend/src/main/java/com/edufocus/edufocus/quiz/repository/QuizRepository.java index 9e7d345..c271ee2 100644 --- a/backend/src/main/java/com/edufocus/edufocus/quiz/repository/QuizRepository.java +++ b/backend/src/main/java/com/edufocus/edufocus/quiz/repository/QuizRepository.java @@ -3,7 +3,6 @@ package com.edufocus.edufocus.quiz.repository; import com.edufocus.edufocus.quiz.entity.Quiz; import org.springframework.data.jpa.repository.JpaRepository; - public interface QuizRepository extends JpaRepository { } diff --git a/backend/src/main/java/com/edufocus/edufocus/quiz/service/ChoiceService.java b/backend/src/main/java/com/edufocus/edufocus/quiz/service/ChoiceService.java new file mode 100644 index 0000000..22b81d1 --- /dev/null +++ b/backend/src/main/java/com/edufocus/edufocus/quiz/service/ChoiceService.java @@ -0,0 +1,7 @@ +package com.edufocus.edufocus.quiz.service; + +import org.springframework.stereotype.Service; + +@Service +public interface ChoiceService { +} diff --git a/backend/src/main/java/com/edufocus/edufocus/quiz/service/ChoiceServiceImpl.java b/backend/src/main/java/com/edufocus/edufocus/quiz/service/ChoiceServiceImpl.java new file mode 100644 index 0000000..8f1b2c0 --- /dev/null +++ b/backend/src/main/java/com/edufocus/edufocus/quiz/service/ChoiceServiceImpl.java @@ -0,0 +1,25 @@ +package com.edufocus.edufocus.quiz.service; + +import com.edufocus.edufocus.quiz.entity.Choice; +import com.edufocus.edufocus.quiz.entity.ChoiceCreateRequest; +import com.edufocus.edufocus.quiz.repository.ChoiceRepository; +import jakarta.transaction.Transactional; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@Transactional +@RequiredArgsConstructor +public class ChoiceServiceImpl implements ChoiceService { + + private final ChoiceRepository choiceRepository; + + public void createChoice(ChoiceCreateRequest choiceCreateRequest) { + Choice choice = new Choice().builder() + .num(choiceCreateRequest.getNum()) + .content(choiceCreateRequest.getContent()) + .build(); + + choiceRepository.save(choice); + } +} diff --git a/backend/src/main/java/com/edufocus/edufocus/quiz/service/QuizService.java b/backend/src/main/java/com/edufocus/edufocus/quiz/service/QuizService.java index 8b0725f..60c81a5 100644 --- a/backend/src/main/java/com/edufocus/edufocus/quiz/service/QuizService.java +++ b/backend/src/main/java/com/edufocus/edufocus/quiz/service/QuizService.java @@ -1,6 +1,7 @@ package com.edufocus.edufocus.quiz.service; import com.edufocus.edufocus.quiz.entity.QuizCreateRequest; +import com.edufocus.edufocus.quiz.entity.QuizSet; import org.springframework.stereotype.Service; import java.util.List; @@ -8,8 +9,7 @@ import java.util.List; @Service public interface QuizService { - void createQuiz(long quizSetId, QuizCreateRequest - QuizCreateRequest); + void createQuiz(QuizSet quizSet, QuizCreateRequest quizCreateRequest); boolean deleteQuiz(long quizId); } diff --git a/backend/src/main/java/com/edufocus/edufocus/quiz/service/QuizServiceImpl.java b/backend/src/main/java/com/edufocus/edufocus/quiz/service/QuizServiceImpl.java index 0412f68..12c9543 100644 --- a/backend/src/main/java/com/edufocus/edufocus/quiz/service/QuizServiceImpl.java +++ b/backend/src/main/java/com/edufocus/edufocus/quiz/service/QuizServiceImpl.java @@ -1,43 +1,50 @@ package com.edufocus.edufocus.quiz.service; -import com.edufocus.edufocus.quiz.entity.Quiz; -import com.edufocus.edufocus.quiz.entity.QuizCreateRequest; -import com.edufocus.edufocus.quiz.entity.QuizSet; -import com.edufocus.edufocus.quiz.entity.QuizType; +import com.edufocus.edufocus.quiz.entity.*; +import com.edufocus.edufocus.quiz.repository.ChoiceRepository; import com.edufocus.edufocus.quiz.repository.QuizRepository; import com.edufocus.edufocus.quiz.repository.QuizSetRepository; import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import java.util.ArrayList; +import java.util.List; + + @Service @Transactional @RequiredArgsConstructor public class QuizServiceImpl implements QuizService { + private final ChoiceRepository choiceRepository; + private final QuizRepository quizRepository; private final QuizSetRepository quizSetRepository; @Override - public void createQuiz(long quizSetId, QuizCreateRequest quizCreateRequest) { - QuizSet quizSet = quizSetRepository.findById(quizSetId).get(); - + public void createQuiz(QuizSet quizSet, QuizCreateRequest quizCreateRequest) { Quiz quiz = new Quiz().builder() - .title(quizCreateRequest.getTitle()) - .description(quizCreateRequest.getDescription()) - .answer(quizCreateRequest.getAnswer()) - .quizType(QuizType.valueOf(quizCreateRequest.getQuizType())) .quizSet(quizSet) + .question(quizCreateRequest.getQuestion()) + .answer(quizCreateRequest.getAnswer()) .build(); - if (!quiz.getQuizType().equals(QuizType.MULTIPLE)) { - quiz.setChoice1(quizCreateRequest.getChoice1()); - quiz.setChoice2(quizCreateRequest.getChoice2()); - quiz.setChoice3(quizCreateRequest.getChoice3()); - quiz.setChoice4(quizCreateRequest.getChoice4()); + List choices = new ArrayList<>(); + + for (ChoiceCreateRequest choiceCreateRequest : quizCreateRequest.getChoices()) { + Choice choice = new Choice().builder() + .quiz(quiz) + .num(choiceCreateRequest.getNum()) + .content(choiceCreateRequest.getContent()) + .build(); + choices.add(choice); + choiceRepository.save(choice); } + quiz.setChoices(choices); + quizRepository.save(quiz); } diff --git a/backend/src/main/java/com/edufocus/edufocus/quiz/service/QuizSetService.java b/backend/src/main/java/com/edufocus/edufocus/quiz/service/QuizSetService.java index a62a979..14f3e2b 100644 --- a/backend/src/main/java/com/edufocus/edufocus/quiz/service/QuizSetService.java +++ b/backend/src/main/java/com/edufocus/edufocus/quiz/service/QuizSetService.java @@ -1,17 +1,20 @@ package com.edufocus.edufocus.quiz.service; import com.edufocus.edufocus.quiz.entity.QuizSet; -import com.edufocus.edufocus.quiz.entity.SetCreateRequest; +import com.edufocus.edufocus.quiz.entity.QuizSetResponse; import org.springframework.stereotype.Service; @Service public interface QuizSetService { - QuizSet createQuizSet(SetCreateRequest setCreateRequest); + QuizSet createQuizSet(Long userId, String title); void updateQuizSet(QuizSet quizSet); void deleteQuizSet(long quizSetId); QuizSet findQuizSet(long quizSetId); + + QuizSetResponse findQuizSetResponse(long quizSetId); + } diff --git a/backend/src/main/java/com/edufocus/edufocus/quiz/service/QuizSetServiceImpl.java b/backend/src/main/java/com/edufocus/edufocus/quiz/service/QuizSetServiceImpl.java index 1836ce5..ee582ee 100644 --- a/backend/src/main/java/com/edufocus/edufocus/quiz/service/QuizSetServiceImpl.java +++ b/backend/src/main/java/com/edufocus/edufocus/quiz/service/QuizSetServiceImpl.java @@ -9,6 +9,9 @@ import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import java.util.ArrayList; +import java.util.List; + @Service @Transactional @RequiredArgsConstructor @@ -18,16 +21,13 @@ public class QuizSetServiceImpl implements QuizSetService { private final UserRepository userRepository; - @Override - public QuizSet createQuizSet(SetCreateRequest setCreateRequest) { + public QuizSet createQuizSet(Long userId, String title) { QuizSet quizSet = new QuizSet(); - User user = userRepository.findById(setCreateRequest.getUserId()).get(); - + User user = userRepository.findById(userId).get(); quizSet.setUser(user); - - quizSet.setTitle(setCreateRequest.getTitle()); + quizSet.setTitle(title); return quizSetRepository.save(quizSet); } @@ -46,4 +46,28 @@ public class QuizSetServiceImpl implements QuizSetService { public QuizSet findQuizSet(long quizSetId) { return quizSetRepository.findById(quizSetId).get(); } + + @Override + public QuizSetResponse findQuizSetResponse(long quizSetId) { + QuizSet quizSet = findQuizSet(quizSetId); + + List quizResponses = new ArrayList<>(); + for (Quiz quiz : quizSet.getQuizzes()) { + QuizResponse quizResponse = new QuizResponse().builder() + .question(quiz.getQuestion()) + .choices(quiz.getChoices()) + .build(); + quizResponses.add(quizResponse); + } + + QuizSetResponse quizSetResponse = new QuizSetResponse().builder() + .title(quizSet.getTitle()) + .image(quizSet.getImage()) + .quizzes(quizResponses) + .build(); + + return quizSetResponse; + } + + } diff --git a/backend/src/main/java/com/edufocus/edufocus/registration/controller/RegistrationController.java b/backend/src/main/java/com/edufocus/edufocus/registration/controller/RegistrationController.java index 1d28aaf..ee36efb 100644 --- a/backend/src/main/java/com/edufocus/edufocus/registration/controller/RegistrationController.java +++ b/backend/src/main/java/com/edufocus/edufocus/registration/controller/RegistrationController.java @@ -26,11 +26,9 @@ public class RegistrationController { Long lectureId = map.get("lectureId"); if (!registrationServiceImpl.createRegistration(userId, lectureId)) { - String msg = new String("Duplicated Registration"); - return new ResponseEntity<>(msg, HttpStatus.CONFLICT); + return new ResponseEntity<>(HttpStatus.CONFLICT); } - String msg = new String("registration successful"); return new ResponseEntity<>(HttpStatus.CREATED); } From 78b37baf9f57b01a5e3bf94e4920efd3fd860af8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A0=95=EB=AF=BC?= Date: Tue, 30 Jul 2024 09:32:37 +0900 Subject: [PATCH 23/32] =?UTF-8?q?feat:=20openvidu=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/edufocus/edufocus/video/controller/Controller.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/src/main/java/com/edufocus/edufocus/video/controller/Controller.java b/backend/src/main/java/com/edufocus/edufocus/video/controller/Controller.java index 6ad9ef3..c4fa32c 100644 --- a/backend/src/main/java/com/edufocus/edufocus/video/controller/Controller.java +++ b/backend/src/main/java/com/edufocus/edufocus/video/controller/Controller.java @@ -139,4 +139,6 @@ public class Controller { return ResponseEntity.ok("ok"); } + + } From bfa6387a726c1c171871084fbd5eb41cf96ecaac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A0=95=EB=AF=BC?= Date: Tue, 30 Jul 2024 13:04:45 +0900 Subject: [PATCH 24/32] =?UTF-8?q?feat:=20=EC=9C=A0=EC=A0=80=20=ED=9A=8C?= =?UTF-8?q?=EC=9B=90=EC=A0=95=EB=B3=B4=20=EC=88=98=EC=A0=95=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../user/controller/UserController.java | 25 +++++ .../edufocus/user/model/entity/InfoDto.java | 7 ++ .../user/model/entity/PasswordDto.java | 14 +++ .../user/model/service/UserService.java | 6 +- .../user/model/service/UserServiceImpl.java | 98 +++++++++++-------- 5 files changed, 107 insertions(+), 43 deletions(-) create mode 100644 backend/src/main/java/com/edufocus/edufocus/user/model/entity/PasswordDto.java diff --git a/backend/src/main/java/com/edufocus/edufocus/user/controller/UserController.java b/backend/src/main/java/com/edufocus/edufocus/user/controller/UserController.java index 5b36459..7ca0bf4 100644 --- a/backend/src/main/java/com/edufocus/edufocus/user/controller/UserController.java +++ b/backend/src/main/java/com/edufocus/edufocus/user/controller/UserController.java @@ -1,5 +1,7 @@ package com.edufocus.edufocus.user.controller; +import com.edufocus.edufocus.user.model.entity.InfoDto; +import com.edufocus.edufocus.user.model.entity.PasswordDto; import com.edufocus.edufocus.user.model.entity.User; import com.edufocus.edufocus.user.model.service.UserService; import com.edufocus.edufocus.user.util.JWTUtil; @@ -42,7 +44,30 @@ public class UserController { } + @PutMapping("/updateinfo/{id}") + public ResponseEntity updateUserInfo( + @PathVariable("id") Long id, + @RequestBody InfoDto infoDto) { + try { + userService.changeuInfo(infoDto, id); + return ResponseEntity.ok("User info updated successfully"); + } catch (Exception e) { + return ResponseEntity.badRequest().body(e.getMessage()); + } + } + // 비밀번호 변경 + @PutMapping("/updatepassword/{id}") + public ResponseEntity updatePassword( + @PathVariable("id") Long id, + @RequestBody PasswordDto passwordDto) { + try { + userService.changePassword(passwordDto, id); + return ResponseEntity.ok("Password changed successfully"); + } catch (Exception e) { + return ResponseEntity.badRequest().body(e.getMessage()); + } + } @Operation(summary = "로그인", description = "아이디와 비밀번호를 이용하여 로그인 처리.") @PostMapping("/login") public ResponseEntity> login( diff --git a/backend/src/main/java/com/edufocus/edufocus/user/model/entity/InfoDto.java b/backend/src/main/java/com/edufocus/edufocus/user/model/entity/InfoDto.java index ed96b9c..52b1f08 100644 --- a/backend/src/main/java/com/edufocus/edufocus/user/model/entity/InfoDto.java +++ b/backend/src/main/java/com/edufocus/edufocus/user/model/entity/InfoDto.java @@ -1,8 +1,15 @@ package com.edufocus.edufocus.user.model.entity; +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter + public class InfoDto { String name; String email; + } diff --git a/backend/src/main/java/com/edufocus/edufocus/user/model/entity/PasswordDto.java b/backend/src/main/java/com/edufocus/edufocus/user/model/entity/PasswordDto.java new file mode 100644 index 0000000..a77f73e --- /dev/null +++ b/backend/src/main/java/com/edufocus/edufocus/user/model/entity/PasswordDto.java @@ -0,0 +1,14 @@ +package com.edufocus.edufocus.user.model.entity; + +import lombok.Generated; +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +public class PasswordDto { + + String currentPassword; + String newPassword; + String newPasswordCheck; +} diff --git a/backend/src/main/java/com/edufocus/edufocus/user/model/service/UserService.java b/backend/src/main/java/com/edufocus/edufocus/user/model/service/UserService.java index 2f63135..5e1ba53 100644 --- a/backend/src/main/java/com/edufocus/edufocus/user/model/service/UserService.java +++ b/backend/src/main/java/com/edufocus/edufocus/user/model/service/UserService.java @@ -1,5 +1,7 @@ package com.edufocus.edufocus.user.model.service; +import com.edufocus.edufocus.user.model.entity.InfoDto; +import com.edufocus.edufocus.user.model.entity.PasswordDto; import com.edufocus.edufocus.user.model.entity.User; public interface UserService { @@ -12,7 +14,7 @@ public interface UserService { void sendEamail(User user) throws Exception; void userCheck(Long id) throws Exception; String getUserName(Long id) throws Exception; - void changeuInfo(Long id) throws Exception; - void changePassword(Long id) throws Exception; + void changeuInfo(InfoDto infoDto,Long id) throws Exception; + void changePassword(PasswordDto passwordDto,Long id) throws Exception; } diff --git a/backend/src/main/java/com/edufocus/edufocus/user/model/service/UserServiceImpl.java b/backend/src/main/java/com/edufocus/edufocus/user/model/service/UserServiceImpl.java index 66bb873..ed0f07d 100644 --- a/backend/src/main/java/com/edufocus/edufocus/user/model/service/UserServiceImpl.java +++ b/backend/src/main/java/com/edufocus/edufocus/user/model/service/UserServiceImpl.java @@ -1,9 +1,7 @@ package com.edufocus.edufocus.user.model.service; -import com.edufocus.edufocus.user.model.entity.MailDto; -import com.edufocus.edufocus.user.model.entity.MemberChangeDto; -import com.edufocus.edufocus.user.model.entity.User; +import com.edufocus.edufocus.user.model.entity.*; import com.edufocus.edufocus.user.model.exception.UserException; import com.edufocus.edufocus.user.model.repository.UserRepository; import jakarta.transaction.Transactional; @@ -21,46 +19,38 @@ import java.util.Optional; @Service @Transactional @RequiredArgsConstructor -public class UserServiceImpl implements UserService{ +public class UserServiceImpl implements UserService { private final UserRepository userRepository; private final JavaMailSender mailSender; - - public void join(User user) - { + public void join(User user) { userRepository.save(user); } - public User login(User user) throws SQLException - { + public User login(User user) throws SQLException { Optional findUser = userRepository.findByUserId(user.getUserId()); - if(findUser.isEmpty()) - { + if (findUser.isEmpty()) { throw new UserException("없는 유저"); } - if(findUser.isPresent()) - { + if (findUser.isPresent()) { User find = findUser.get(); - if(find.getPassword().equals(user.getPassword())) - { + if (find.getPassword().equals(user.getPassword())) { return find; - } - else{ + } else { throw new UserException("비밀번호 틀림"); } - } - else{ + } else { throw new UserException("없는 유저"); @@ -69,13 +59,10 @@ public class UserServiceImpl implements UserService{ } @Override - public User userInfo(Long id) - { - try{ + public User userInfo(Long id) { + try { return userRepository.findById(id).get(); - } - catch (Exception e) - { + } catch (Exception e) { throw new UserException(e.getMessage()); } @@ -89,12 +76,11 @@ public class UserServiceImpl implements UserService{ SimpleMailMessage message = new SimpleMailMessage(); - message.setTo(mailDto.getAddress()); message.setFrom("passfinder111@gmail.com"); message.setSubject(mailDto.getTitle()); message.setText(mailDto.getMessage()); - System.out.println("!!!!!!!!!!!!!!!!!!"+ message); + System.out.println("!!!!!!!!!!!!!!!!!!" + message); mailSender.send(message); @@ -105,15 +91,15 @@ public class UserServiceImpl implements UserService{ String str = getTempPassword(); MailDto dto = new MailDto(); dto.setAddress(user.getEmail()); - dto.setTitle(user.getUserId()+"님의 임시비밀번호 안내 이메일 입니다."); - dto.setMessage("안녕하세요. EduFoucs 입니다. "+ "\n"+ "임시비밀번호 안내 관련 메일 입니다." + "\n[" + user.getName() + "]" + "님의 임시 비밀번호는 " + dto.setTitle(user.getUserId() + "님의 임시비밀번호 안내 이메일 입니다."); + dto.setMessage("안녕하세요. EduFoucs 입니다. " + "\n" + "임시비밀번호 안내 관련 메일 입니다." + "\n[" + user.getName() + "]" + "님의 임시 비밀번호는 " + str + " 입니다."); System.out.println(dto); - MemberChangeDto memberChangeDto = new MemberChangeDto(user.getId(),str); + MemberChangeDto memberChangeDto = new MemberChangeDto(user.getId(), str); System.out.println(memberChangeDto); - userRepository.updatePassword(memberChangeDto.getId(),memberChangeDto.getPassword()); + userRepository.updatePassword(memberChangeDto.getId(), memberChangeDto.getPassword()); System.out.println(); return dto; @@ -125,13 +111,11 @@ public class UserServiceImpl implements UserService{ User user = userRepository.findById(id).orElse(null); - if(user == null) - { + if (user == null) { System.out.println("불가"); throw new RuntimeException("유효하지 않은 아이디입니다. 다시 입력하세요"); - } - else { + } else { sendEamail(user); } @@ -143,16 +127,48 @@ public class UserServiceImpl implements UserService{ return userRepository.findById(id).get().getName(); } - @Override - public void changeuInfo(Long id) throws Exception { - - } @Override - public void changePassword(Long id) throws Exception { + public void changeuInfo(InfoDto infoDto, Long id) throws Exception { + User user = userRepository.findById(id).orElse(null); + + if (user == null) { + throw new Exception("User not found"); + } + + if (infoDto.getName() != null) + { + user.setName(infoDto.getName()); + } + + if(infoDto.getEmail()!=null) + { + user.setEmail(infoDto.getEmail()); + } + userRepository.save(user); +} + + + @Override + public void changePassword(PasswordDto passwordDto, Long id) throws Exception { + User user = userRepository.findById(id).orElse(null); + + if (user == null) { + throw new Exception("User not found"); + } + + if (!user.getPassword().equals(passwordDto.getCurrentPassword())) { + throw new Exception("Current password is incorrect"); + } else { + if (!passwordDto.getNewPassword().equals(passwordDto.getNewPasswordCheck())) { + throw new Exception("New password confirmation does not match"); + } + } + + user.setPassword(passwordDto.getNewPassword()); + userRepository.save(user); } - public String getTempPassword() { char[] charSet = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'}; From 97c25fff13f8c6c465303028ea12b3f2b2d0d843 Mon Sep 17 00:00:00 2001 From: kgc9007 Date: Tue, 30 Jul 2024 14:23:41 +0900 Subject: [PATCH 25/32] =?UTF-8?q?feat:=20Quiz=20=EC=82=AC=EC=A7=84=20?= =?UTF-8?q?=EB=93=B1=EB=A1=9D,=20=EB=AA=A9=EB=A1=9D=20=EC=A1=B0=ED=9A=8C?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../quiz/controller/QuizController.java | 30 +++++++++++-- .../quiz/entity/MyQuizSetResponse.java | 14 ++++++ .../edufocus/edufocus/quiz/entity/Quiz.java | 2 +- .../edufocus/quiz/entity/QuizSet.java | 7 +-- .../edufocus/quiz/entity/QuizSetResponse.java | 3 -- .../quiz/repository/ChoiceRepository.java | 8 ---- .../quiz/repository/QuizSetRepository.java | 3 ++ .../edufocus/quiz/service/ChoiceService.java | 7 --- .../quiz/service/ChoiceServiceImpl.java | 25 ----------- .../edufocus/quiz/service/QuizService.java | 5 ++- .../quiz/service/QuizServiceImpl.java | 39 ++++++++-------- .../edufocus/quiz/service/QuizSetService.java | 10 +++-- .../quiz/service/QuizSetServiceImpl.java | 45 +++++++++++++++---- 13 files changed, 114 insertions(+), 84 deletions(-) create mode 100644 backend/src/main/java/com/edufocus/edufocus/quiz/entity/MyQuizSetResponse.java delete mode 100644 backend/src/main/java/com/edufocus/edufocus/quiz/repository/ChoiceRepository.java delete mode 100644 backend/src/main/java/com/edufocus/edufocus/quiz/service/ChoiceService.java delete mode 100644 backend/src/main/java/com/edufocus/edufocus/quiz/service/ChoiceServiceImpl.java diff --git a/backend/src/main/java/com/edufocus/edufocus/quiz/controller/QuizController.java b/backend/src/main/java/com/edufocus/edufocus/quiz/controller/QuizController.java index ac58dd2..cf0ecc8 100644 --- a/backend/src/main/java/com/edufocus/edufocus/quiz/controller/QuizController.java +++ b/backend/src/main/java/com/edufocus/edufocus/quiz/controller/QuizController.java @@ -12,9 +12,8 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; -import java.io.File; import java.io.IOException; -import java.util.UUID; +import java.util.List; @RestController @RequestMapping("/quiz") @@ -30,13 +29,14 @@ public class QuizController { @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public ResponseEntity createQuizSet(@RequestHeader("Authorization") String accessToken, @RequestPart QuizSetCreateRequest quizSetCreateRequest - , @RequestPart(value = "image", required = false) MultipartFile image) throws IOException { + , @RequestPart(value = "images", required = false) List images) throws IOException { Long userId = Long.parseLong(jwtUtil.getUserId(accessToken)); QuizSet quizSet = quizSetService.createQuizSet(userId, quizSetCreateRequest.getTitle()); + int imageIdx = 0; for (QuizCreateRequest quizCreateRequest : quizSetCreateRequest.getQuizzes()) { - quizService.createQuiz(quizSet, quizCreateRequest); + quizService.createQuiz(quizSet, quizCreateRequest, images.get(imageIdx++)); } return new ResponseEntity<>(HttpStatus.CREATED); @@ -46,6 +46,28 @@ public class QuizController { public ResponseEntity getQuizzes(@PathVariable Long quizsetId) { QuizSetResponse quizSet = quizSetService.findQuizSetResponse(quizsetId); + if (quizSet == null) { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + return new ResponseEntity<>(quizSet, HttpStatus.OK); } + + @DeleteMapping("/{quizsetId}") + public ResponseEntity deleteQuizSet(@RequestHeader("Authorization") String accessToken, @PathVariable Long quizsetId) { + Long userId = Long.parseLong(jwtUtil.getUserId(accessToken)); + + if (!quizSetService.deleteQuizSet(userId, quizsetId)) { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } + + @GetMapping + public ResponseEntity getQuizSets(@RequestHeader("Authorization") String accessToken) { + Long userId = Long.parseLong(jwtUtil.getUserId(accessToken)); + + return new ResponseEntity<>(quizSetService.findMyQuizSetResponses(userId), HttpStatus.OK); + } } diff --git a/backend/src/main/java/com/edufocus/edufocus/quiz/entity/MyQuizSetResponse.java b/backend/src/main/java/com/edufocus/edufocus/quiz/entity/MyQuizSetResponse.java new file mode 100644 index 0000000..da8947f --- /dev/null +++ b/backend/src/main/java/com/edufocus/edufocus/quiz/entity/MyQuizSetResponse.java @@ -0,0 +1,14 @@ +package com.edufocus.edufocus.quiz.entity; + +import lombok.*; + +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class MyQuizSetResponse { + + private Long quizSetId; + + private String title; +} diff --git a/backend/src/main/java/com/edufocus/edufocus/quiz/entity/Quiz.java b/backend/src/main/java/com/edufocus/edufocus/quiz/entity/Quiz.java index f9a3cc7..7eed889 100644 --- a/backend/src/main/java/com/edufocus/edufocus/quiz/entity/Quiz.java +++ b/backend/src/main/java/com/edufocus/edufocus/quiz/entity/Quiz.java @@ -33,7 +33,7 @@ public class Quiz { @Column private String answer; - @OneToMany(mappedBy = "quiz") + @OneToMany(mappedBy = "quiz", orphanRemoval = true, cascade = CascadeType.ALL) @JsonManagedReference private List choices; diff --git a/backend/src/main/java/com/edufocus/edufocus/quiz/entity/QuizSet.java b/backend/src/main/java/com/edufocus/edufocus/quiz/entity/QuizSet.java index 89a2b4d..8357279 100644 --- a/backend/src/main/java/com/edufocus/edufocus/quiz/entity/QuizSet.java +++ b/backend/src/main/java/com/edufocus/edufocus/quiz/entity/QuizSet.java @@ -27,12 +27,9 @@ public class QuizSet { @Column private String title; - @Column - private String image; - - @OneToMany(mappedBy = "quizSet") + @OneToMany(mappedBy = "quizSet", orphanRemoval = true) @JsonManagedReference - private List quizzes = new ArrayList(); + private List quizzes = new ArrayList(); public void addQuiz(Quiz quiz) { this.quizzes.add(quiz); diff --git a/backend/src/main/java/com/edufocus/edufocus/quiz/entity/QuizSetResponse.java b/backend/src/main/java/com/edufocus/edufocus/quiz/entity/QuizSetResponse.java index 9fdaf5e..1d4098d 100644 --- a/backend/src/main/java/com/edufocus/edufocus/quiz/entity/QuizSetResponse.java +++ b/backend/src/main/java/com/edufocus/edufocus/quiz/entity/QuizSetResponse.java @@ -2,7 +2,6 @@ package com.edufocus.edufocus.quiz.entity; import lombok.*; -import java.util.ArrayList; import java.util.List; @Getter @@ -14,7 +13,5 @@ public class QuizSetResponse { private String title; - private String image; - private List quizzes; } diff --git a/backend/src/main/java/com/edufocus/edufocus/quiz/repository/ChoiceRepository.java b/backend/src/main/java/com/edufocus/edufocus/quiz/repository/ChoiceRepository.java deleted file mode 100644 index 740dda2..0000000 --- a/backend/src/main/java/com/edufocus/edufocus/quiz/repository/ChoiceRepository.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.edufocus.edufocus.quiz.repository; - -import com.edufocus.edufocus.quiz.entity.Choice; -import org.springframework.data.jpa.repository.JpaRepository; - -public interface ChoiceRepository extends JpaRepository { - -} diff --git a/backend/src/main/java/com/edufocus/edufocus/quiz/repository/QuizSetRepository.java b/backend/src/main/java/com/edufocus/edufocus/quiz/repository/QuizSetRepository.java index 81883a6..89bc7bc 100644 --- a/backend/src/main/java/com/edufocus/edufocus/quiz/repository/QuizSetRepository.java +++ b/backend/src/main/java/com/edufocus/edufocus/quiz/repository/QuizSetRepository.java @@ -3,6 +3,9 @@ package com.edufocus.edufocus.quiz.repository; import com.edufocus.edufocus.quiz.entity.QuizSet; import org.springframework.data.jpa.repository.JpaRepository; +import java.util.List; + public interface QuizSetRepository extends JpaRepository { + List findQuizSetsByUserId(Long userId); } diff --git a/backend/src/main/java/com/edufocus/edufocus/quiz/service/ChoiceService.java b/backend/src/main/java/com/edufocus/edufocus/quiz/service/ChoiceService.java deleted file mode 100644 index 22b81d1..0000000 --- a/backend/src/main/java/com/edufocus/edufocus/quiz/service/ChoiceService.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.edufocus.edufocus.quiz.service; - -import org.springframework.stereotype.Service; - -@Service -public interface ChoiceService { -} diff --git a/backend/src/main/java/com/edufocus/edufocus/quiz/service/ChoiceServiceImpl.java b/backend/src/main/java/com/edufocus/edufocus/quiz/service/ChoiceServiceImpl.java deleted file mode 100644 index 8f1b2c0..0000000 --- a/backend/src/main/java/com/edufocus/edufocus/quiz/service/ChoiceServiceImpl.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.edufocus.edufocus.quiz.service; - -import com.edufocus.edufocus.quiz.entity.Choice; -import com.edufocus.edufocus.quiz.entity.ChoiceCreateRequest; -import com.edufocus.edufocus.quiz.repository.ChoiceRepository; -import jakarta.transaction.Transactional; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; - -@Service -@Transactional -@RequiredArgsConstructor -public class ChoiceServiceImpl implements ChoiceService { - - private final ChoiceRepository choiceRepository; - - public void createChoice(ChoiceCreateRequest choiceCreateRequest) { - Choice choice = new Choice().builder() - .num(choiceCreateRequest.getNum()) - .content(choiceCreateRequest.getContent()) - .build(); - - choiceRepository.save(choice); - } -} diff --git a/backend/src/main/java/com/edufocus/edufocus/quiz/service/QuizService.java b/backend/src/main/java/com/edufocus/edufocus/quiz/service/QuizService.java index 60c81a5..92014a2 100644 --- a/backend/src/main/java/com/edufocus/edufocus/quiz/service/QuizService.java +++ b/backend/src/main/java/com/edufocus/edufocus/quiz/service/QuizService.java @@ -3,13 +3,14 @@ package com.edufocus.edufocus.quiz.service; import com.edufocus.edufocus.quiz.entity.QuizCreateRequest; import com.edufocus.edufocus.quiz.entity.QuizSet; import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; +import java.io.IOException; import java.util.List; @Service public interface QuizService { - void createQuiz(QuizSet quizSet, QuizCreateRequest quizCreateRequest); + void createQuiz(QuizSet quizSet, QuizCreateRequest quizCreateRequest, MultipartFile quizImage) throws IOException; - boolean deleteQuiz(long quizId); } diff --git a/backend/src/main/java/com/edufocus/edufocus/quiz/service/QuizServiceImpl.java b/backend/src/main/java/com/edufocus/edufocus/quiz/service/QuizServiceImpl.java index 12c9543..b25adfb 100644 --- a/backend/src/main/java/com/edufocus/edufocus/quiz/service/QuizServiceImpl.java +++ b/backend/src/main/java/com/edufocus/edufocus/quiz/service/QuizServiceImpl.java @@ -1,15 +1,17 @@ package com.edufocus.edufocus.quiz.service; import com.edufocus.edufocus.quiz.entity.*; -import com.edufocus.edufocus.quiz.repository.ChoiceRepository; import com.edufocus.edufocus.quiz.repository.QuizRepository; -import com.edufocus.edufocus.quiz.repository.QuizSetRepository; import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; +import java.io.File; +import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.UUID; @Service @@ -17,14 +19,10 @@ import java.util.List; @RequiredArgsConstructor public class QuizServiceImpl implements QuizService { - private final ChoiceRepository choiceRepository; - private final QuizRepository quizRepository; - private final QuizSetRepository quizSetRepository; - @Override - public void createQuiz(QuizSet quizSet, QuizCreateRequest quizCreateRequest) { + public void createQuiz(QuizSet quizSet, QuizCreateRequest quizCreateRequest, MultipartFile quizImage) throws IOException { Quiz quiz = new Quiz().builder() .quizSet(quizSet) .question(quizCreateRequest.getQuestion()) @@ -40,23 +38,28 @@ public class QuizServiceImpl implements QuizService { .content(choiceCreateRequest.getContent()) .build(); choices.add(choice); - choiceRepository.save(choice); } quiz.setChoices(choices); + if (quizImage != null && !quizImage.isEmpty()) { + String uid = UUID.randomUUID().toString(); + + String currentPath = "backend/src/main/resources/images/quizzes/"; + File checkPathFile = new File(currentPath); + if (!checkPathFile.exists()) { + checkPathFile.mkdirs(); + } + + File savingImage = new File(currentPath + uid + "_" + quizImage.getOriginalFilename()); + quizImage.transferTo(savingImage.toPath()); + String savePath = savingImage.toPath().toString(); + + quiz.setImage(savePath); + } + quizRepository.save(quiz); } - @Override - public boolean deleteQuiz(long quizId) { - // 유저 아이디 정보 조회 후 검증 로직 추가 예정 - // jwt -> 로그인 유저 정보 조회 - // quizId -> 퀴즈 정보 조회 -> 퀴즈셋 정보 조회 - // 퀴즈셋 생성자와 로그인 유저의 id값이 일치하는지 확인 -> 불일치시 삭제 실패 - - quizRepository.deleteById(quizId); - return true; - } } diff --git a/backend/src/main/java/com/edufocus/edufocus/quiz/service/QuizSetService.java b/backend/src/main/java/com/edufocus/edufocus/quiz/service/QuizSetService.java index 14f3e2b..1f8e4d7 100644 --- a/backend/src/main/java/com/edufocus/edufocus/quiz/service/QuizSetService.java +++ b/backend/src/main/java/com/edufocus/edufocus/quiz/service/QuizSetService.java @@ -1,9 +1,12 @@ package com.edufocus.edufocus.quiz.service; +import com.edufocus.edufocus.quiz.entity.MyQuizSetResponse; import com.edufocus.edufocus.quiz.entity.QuizSet; import com.edufocus.edufocus.quiz.entity.QuizSetResponse; import org.springframework.stereotype.Service; +import java.util.List; + @Service public interface QuizSetService { @@ -11,10 +14,11 @@ public interface QuizSetService { void updateQuizSet(QuizSet quizSet); - void deleteQuizSet(long quizSetId); + boolean deleteQuizSet(Long userId, Long quizSetId); - QuizSet findQuizSet(long quizSetId); + QuizSet findQuizSet(Long quizSetId); - QuizSetResponse findQuizSetResponse(long quizSetId); + QuizSetResponse findQuizSetResponse(Long quizSetId); + List findMyQuizSetResponses(Long userId); } diff --git a/backend/src/main/java/com/edufocus/edufocus/quiz/service/QuizSetServiceImpl.java b/backend/src/main/java/com/edufocus/edufocus/quiz/service/QuizSetServiceImpl.java index ee582ee..c65a071 100644 --- a/backend/src/main/java/com/edufocus/edufocus/quiz/service/QuizSetServiceImpl.java +++ b/backend/src/main/java/com/edufocus/edufocus/quiz/service/QuizSetServiceImpl.java @@ -1,7 +1,6 @@ package com.edufocus.edufocus.quiz.service; import com.edufocus.edufocus.quiz.entity.*; -import com.edufocus.edufocus.quiz.repository.QuizRepository; import com.edufocus.edufocus.quiz.repository.QuizSetRepository; import com.edufocus.edufocus.user.model.entity.User; import com.edufocus.edufocus.user.model.repository.UserRepository; @@ -11,6 +10,7 @@ import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.List; +import java.util.Optional; @Service @Transactional @@ -38,36 +38,65 @@ public class QuizSetServiceImpl implements QuizSetService { } @Override - public void deleteQuizSet(long quizSetId) { + public boolean deleteQuizSet(Long userId, Long quizSetId) { + Optional quizSet = quizSetRepository.findById(quizSetId); + + if (quizSet.isEmpty() || userId != quizSet.get().getUser().getId()) { + return false; + } + quizSetRepository.deleteById(quizSetId); + return true; } @Override - public QuizSet findQuizSet(long quizSetId) { + public QuizSet findQuizSet(Long quizSetId) { return quizSetRepository.findById(quizSetId).get(); } @Override - public QuizSetResponse findQuizSetResponse(long quizSetId) { - QuizSet quizSet = findQuizSet(quizSetId); + public QuizSetResponse findQuizSetResponse(Long quizSetId) { + Optional quizSet = quizSetRepository.findById(quizSetId); + + if (quizSet.isEmpty()) { + return null; + } + List quizResponses = new ArrayList<>(); - for (Quiz quiz : quizSet.getQuizzes()) { + for (Quiz quiz : quizSet.get().getQuizzes()) { QuizResponse quizResponse = new QuizResponse().builder() .question(quiz.getQuestion()) + .image(quiz.getImage()) .choices(quiz.getChoices()) .build(); quizResponses.add(quizResponse); } QuizSetResponse quizSetResponse = new QuizSetResponse().builder() - .title(quizSet.getTitle()) - .image(quizSet.getImage()) + .title(quizSet.get().getTitle()) .quizzes(quizResponses) .build(); return quizSetResponse; } + @Override + public List findMyQuizSetResponses(Long userId) { + List quizSetList = quizSetRepository.findQuizSetsByUserId(userId); + + List myQuizSetResponses = new ArrayList<>(); + for (QuizSet quizSet : quizSetList) { + MyQuizSetResponse myQuizSetResponse = new MyQuizSetResponse().builder() + .quizSetId(quizSet.getId()) + .title(quizSet.getTitle()) + .build(); + + myQuizSetResponses.add(myQuizSetResponse); + } + + return myQuizSetResponses; + + } } From 96a124a82200b84ba9c82b89ca02de24023ede13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A0=95=EB=AF=BC?= Date: Tue, 30 Jul 2024 15:00:31 +0900 Subject: [PATCH 26/32] =?UTF-8?q?feat:=20user=20login=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../user/controller/UserController.java | 5 +- .../user/model/service/UserServiceImpl.java | 5 +- .../edufocus/user/UserControllerTest.java | 79 +++++ .../model/service/UserServiceImplTest.java | 298 ++++++++++++++++++ 4 files changed, 385 insertions(+), 2 deletions(-) create mode 100644 backend/src/test/java/com/edufocus/edufocus/user/UserControllerTest.java create mode 100644 backend/src/test/java/com/edufocus/edufocus/user/model/service/UserServiceImplTest.java diff --git a/backend/src/main/java/com/edufocus/edufocus/user/controller/UserController.java b/backend/src/main/java/com/edufocus/edufocus/user/controller/UserController.java index 7ca0bf4..b4bff53 100644 --- a/backend/src/main/java/com/edufocus/edufocus/user/controller/UserController.java +++ b/backend/src/main/java/com/edufocus/edufocus/user/controller/UserController.java @@ -86,9 +86,12 @@ public class UserController { String accessToken = jwtUtil.createAccessToken(String.valueOf(loginUser.getId())); String refreshToken = jwtUtil.createRefreshToken(String.valueOf(loginUser.getId())); + userService.saveRefreshToken(loginUser.getId(), refreshToken); - System.out.println(accessToken); + + + resultMap.put("role",loginUser.getRole()); resultMap.put("access-token", accessToken); diff --git a/backend/src/main/java/com/edufocus/edufocus/user/model/service/UserServiceImpl.java b/backend/src/main/java/com/edufocus/edufocus/user/model/service/UserServiceImpl.java index ed0f07d..716c4f0 100644 --- a/backend/src/main/java/com/edufocus/edufocus/user/model/service/UserServiceImpl.java +++ b/backend/src/main/java/com/edufocus/edufocus/user/model/service/UserServiceImpl.java @@ -26,7 +26,10 @@ public class UserServiceImpl implements UserService { private final JavaMailSender mailSender; - public void join(User user) { + public void join(User user) + { + System.out.println(user.getRole().getClass()); + userRepository.save(user); } diff --git a/backend/src/test/java/com/edufocus/edufocus/user/UserControllerTest.java b/backend/src/test/java/com/edufocus/edufocus/user/UserControllerTest.java new file mode 100644 index 0000000..eef3a64 --- /dev/null +++ b/backend/src/test/java/com/edufocus/edufocus/user/UserControllerTest.java @@ -0,0 +1,79 @@ +//package com.edufocus.edufocus.user; +// +//import com.edufocus.edufocus.user.controller.UserController; +//import com.edufocus.edufocus.user.model.entity.InfoDto; +//import com.edufocus.edufocus.user.model.entity.PasswordDto; +//import com.edufocus.edufocus.user.model.entity.User; +//import com.edufocus.edufocus.user.model.entity.UserRole; +//import com.edufocus.edufocus.user.model.service.UserService; +//import com.edufocus.edufocus.user.util.JWTUtil; +//import com.fasterxml.jackson.databind.ObjectMapper; +//import org.junit.jupiter.api.BeforeEach; +//import org.junit.jupiter.api.Test; +//import org.junit.jupiter.api.extension.ExtendWith; +//import org.mockito.InjectMocks; +//import org.mockito.Mock; +//import org.mockito.MockitoAnnotations; +//import org.mockito.junit.jupiter.MockitoExtension; +//import org.springframework.beans.factory.annotation.Autowired; +//import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +//import org.springframework.http.HttpStatus; +//import org.springframework.http.MediaType; +//import org.springframework.http.ResponseEntity; +//import org.springframework.test.web.servlet.MockMvc; +// +//import static org.mockito.ArgumentMatchers.any; +//import static org.mockito.Mockito.*; +//import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +// +//@WebMvcTest(UserController.class) +//public class UserControllerTest { +// +// @Mock +// private MockMvc mockMvc; +// +// @Autowired +// ObjectMapper mapper; +// @Mock +// private UserService userService; +// +// @Mock +// private JWTUtil jwtUtil; +// +// @InjectMocks +// private UserController userController; +// +// private User testUser; +// private InfoDto testInfo; +// private PasswordDto passwordDto; +// private UserRole userRole; +// @BeforeEach +// void setUp() +// { +// MockitoAnnotations.openMocks(this); +// +// } +//// @Test +//// void testjoin() throws Exception +//// { +//// +//// User user = new User(); +//// user.setId(1L); +//// user.setName("jungmin"); +//// user.setUserId("v_v"); +//// user.setRole(userRole.STUDENT); +//// user.setPassword("pwd"); +//// user.setEmail("wjdals231@naver.com"); +//// +//// String body= mapper.writeValueAsString( +//// UserRe +//// ) +//// +//// mockMvc.perform(post("/api/user/join") +//// .contentType(MediaType.APPLICATION_JSON) +//// . +//// +//// // +//// +//// } +//} diff --git a/backend/src/test/java/com/edufocus/edufocus/user/model/service/UserServiceImplTest.java b/backend/src/test/java/com/edufocus/edufocus/user/model/service/UserServiceImplTest.java new file mode 100644 index 0000000..9fb092b --- /dev/null +++ b/backend/src/test/java/com/edufocus/edufocus/user/model/service/UserServiceImplTest.java @@ -0,0 +1,298 @@ +package com.edufocus.edufocus.user.model.service; + +import static org.junit.jupiter.api.Assertions.*; + + +import com.edufocus.edufocus.user.model.entity.*; +import com.edufocus.edufocus.user.model.exception.UserException; +import com.edufocus.edufocus.user.model.repository.UserRepository; +import jakarta.transaction.Transactional; +import lombok.RequiredArgsConstructor; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.*; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.mail.SimpleMailMessage; +import org.springframework.mail.javamail.JavaMailSender; + +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +public class UserServiceImplTest { + + @InjectMocks + private UserServiceImpl userService; + + @Mock + private UserRepository userRepository; + + @Mock + private JavaMailSender mailSender; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + } + + @Test + void testJoin() { + User user = new User(); + user.setId(1L); + user.setName("John Doe"); + + userService.join(user); + + verify(userRepository, times(1)).save(user); + } + + @Test + void testLoginSuccess() throws Exception { + User user = new User(); + user.setUserId("john"); + user.setPassword("password"); + + User returnedUser = new User(); + returnedUser.setUserId("john"); + returnedUser.setPassword("password"); + + when(userRepository.findByUserId(user.getUserId())).thenReturn(Optional.of(returnedUser)); + + User result = userService.login(user); + + assertNotNull(result); + assertEquals("john", result.getUserId()); + assertEquals("password", result.getPassword()); + } + + @Test + void testLoginUserNotFound() { + User user = new User(); + user.setUserId("john"); + user.setPassword("password"); + + when(userRepository.findByUserId(user.getUserId())).thenReturn(Optional.empty()); + + UserException exception = assertThrows(UserException.class, () -> userService.login(user)); + + assertEquals("없는 유저", exception.getMessage()); + } + + @Test + void testLoginIncorrectPassword() { + User user = new User(); + user.setUserId("john"); + user.setPassword("password"); + + User returnedUser = new User(); + returnedUser.setUserId("john"); + returnedUser.setPassword("wrongPassword"); + + when(userRepository.findByUserId(user.getUserId())).thenReturn(Optional.of(returnedUser)); + + UserException exception = assertThrows(UserException.class, () -> userService.login(user)); + + assertEquals("비밀번호 틀림", exception.getMessage()); + } + + @Test + void testUserInfo() { + User user = new User(); + user.setId(1L); + user.setName("John Doe"); + + when(userRepository.findById(1L)).thenReturn(Optional.of(user)); + + User result = userService.userInfo(1L); + + assertNotNull(result); + assertEquals("John Doe", result.getName()); + } + + @Test + void testSendEmail() throws Exception { + User user = new User(); + user.setEmail("test@example.com"); + user.setUserId("testUser"); + user.setName("Test User"); + + MailDto mailDto = new MailDto(); + mailDto.setAddress("test@example.com"); + mailDto.setTitle("Test Title"); + mailDto.setMessage("Test Message"); + + doNothing().when(mailSender).send(any(SimpleMailMessage.class)); + doReturn(mailDto).when(userService).createMailAndChargePassword(user); + + userService.sendEamail(user); + + verify(mailSender, times(1)).send(any(SimpleMailMessage.class)); + } + + @Test + void testUserCheck() throws Exception { + User user = new User(); + user.setId(1L); + user.setUserId("testUser"); + user.setEmail("test@example.com"); + user.setName("Test User"); + + when(userRepository.findById(1L)).thenReturn(Optional.of(user)); + + userService.userCheck(1L); + + verify(userRepository, times(1)).findById(1L); + verify(userService, times(1)).sendEamail(user); + } + + @Test + void testUserCheckInvalidId() { + when(userRepository.findById(1L)).thenReturn(Optional.empty()); + + RuntimeException exception = assertThrows(RuntimeException.class, () -> userService.userCheck(1L)); + + assertEquals("유효하지 않은 아이디입니다. 다시 입력하세요", exception.getMessage()); + } + + @Test + void testChangeUserInfo() throws Exception { + Long userId = 1L; + InfoDto infoDto = new InfoDto(); + infoDto.setName("New Name"); + infoDto.setEmail("new.email@example.com"); + + User user = new User(); + user.setId(userId); + user.setName("Old Name"); + user.setEmail("old.email@example.com"); + + when(userRepository.findById(userId)).thenReturn(Optional.of(user)); + + userService.changeuInfo(infoDto, userId); + + assertEquals("New Name", user.getName()); + assertEquals("new.email@example.com", user.getEmail()); + verify(userRepository, times(1)).save(user); + } + + @Test + void testChangeUserInfoUserNotFound() { + Long userId = 1L; + InfoDto infoDto = new InfoDto(); + infoDto.setName("New Name"); + infoDto.setEmail("new.email@example.com"); + + when(userRepository.findById(userId)).thenReturn(Optional.empty()); + + Exception exception = assertThrows(Exception.class, () -> userService.changeuInfo(infoDto, userId)); + + assertEquals("User not found", exception.getMessage()); + } + + @Test + void testChangePasswordSuccess() throws Exception { + Long userId = 1L; + PasswordDto passwordDto = new PasswordDto(); + passwordDto.setCurrentPassword("oldPassword"); + passwordDto.setNewPassword("newPassword"); + passwordDto.setNewPasswordCheck("newPassword"); + + User user = new User(); + user.setId(userId); + user.setPassword("oldPassword"); + + when(userRepository.findById(userId)).thenReturn(Optional.of(user)); + + userService.changePassword(passwordDto, userId); + + assertEquals("newPassword", user.getPassword()); + verify(userRepository, times(1)).save(user); + } + + @Test + void testChangePasswordUserNotFound() { + Long userId = 1L; + PasswordDto passwordDto = new PasswordDto(); + passwordDto.setCurrentPassword("oldPassword"); + passwordDto.setNewPassword("newPassword"); + passwordDto.setNewPasswordCheck("newPassword"); + + when(userRepository.findById(userId)).thenReturn(Optional.empty()); + + Exception exception = assertThrows(Exception.class, () -> userService.changePassword(passwordDto, userId)); + + assertEquals("User not found", exception.getMessage()); + } + + @Test + void testChangePasswordIncorrectCurrentPassword() { + Long userId = 1L; + PasswordDto passwordDto = new PasswordDto(); + passwordDto.setCurrentPassword("wrongPassword"); + passwordDto.setNewPassword("newPassword"); + passwordDto.setNewPasswordCheck("newPassword"); + + User user = new User(); + user.setId(userId); + user.setPassword("oldPassword"); + + when(userRepository.findById(userId)).thenReturn(Optional.of(user)); + + Exception exception = assertThrows(Exception.class, () -> userService.changePassword(passwordDto, userId)); + + assertEquals("Current password is incorrect", exception.getMessage()); + } + + @Test + void testChangePasswordNewPasswordMismatch() { + Long userId = 1L; + PasswordDto passwordDto = new PasswordDto(); + passwordDto.setCurrentPassword("oldPassword"); + passwordDto.setNewPassword("newPassword"); + passwordDto.setNewPasswordCheck("differentNewPassword"); + + User user = new User(); + user.setId(userId); + user.setPassword("oldPassword"); + + when(userRepository.findById(userId)).thenReturn(Optional.of(user)); + + Exception exception = assertThrows(Exception.class, () -> userService.changePassword(passwordDto, userId)); + + assertEquals("New password confirmation does not match", exception.getMessage()); + } + + @Test + void testSaveRefreshToken() throws Exception { + Long userId = 1L; + String refreshToken = "refresh-token"; + + doNothing().when(userRepository).saveRefreshToken(userId, refreshToken); + + userService.saveRefreshToken(userId, refreshToken); + + verify(userRepository, times(1)).saveRefreshToken(userId, refreshToken); + } + + @Test + void testGetRefreshToken() throws Exception { + Long userId = 1L; + String refreshToken = "refresh-token"; + + when(userRepository.getRefreshToken(userId)).thenReturn(refreshToken); + + String result = userService.getRefreshToken(userId); + + assertEquals(refreshToken, result); + } + +// @Test +// void testDeleteRefreshToken() throws Exception { +// Long userId = 1L; +// +// doNothing().when(userRepository).delete +// } +} \ No newline at end of file From 4573f4335563ab79cd47e7f95f7252099cd5178a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A0=95=EB=AF=BC?= Date: Tue, 30 Jul 2024 15:38:42 +0900 Subject: [PATCH 27/32] =?UTF-8?q?feat:=20user=20test=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../user/model/service/UserServiceImpl.java | 9 +- .../edufocus/user/UserControllerTest.java | 79 ---- .../model/service/UserServiceImplTest.java | 361 ++++++------------ 3 files changed, 127 insertions(+), 322 deletions(-) delete mode 100644 backend/src/test/java/com/edufocus/edufocus/user/UserControllerTest.java diff --git a/backend/src/main/java/com/edufocus/edufocus/user/model/service/UserServiceImpl.java b/backend/src/main/java/com/edufocus/edufocus/user/model/service/UserServiceImpl.java index 716c4f0..06ee58a 100644 --- a/backend/src/main/java/com/edufocus/edufocus/user/model/service/UserServiceImpl.java +++ b/backend/src/main/java/com/edufocus/edufocus/user/model/service/UserServiceImpl.java @@ -75,7 +75,7 @@ public class UserServiceImpl implements UserService { public void sendEamail(User user) throws Exception { MailDto mailDto = createMailAndChargePassword(user); - System.out.println("이메일 전송 완료"); + SimpleMailMessage message = new SimpleMailMessage(); @@ -83,7 +83,7 @@ public class UserServiceImpl implements UserService { message.setFrom("passfinder111@gmail.com"); message.setSubject(mailDto.getTitle()); message.setText(mailDto.getMessage()); - System.out.println("!!!!!!!!!!!!!!!!!!" + message); + mailSender.send(message); @@ -101,9 +101,7 @@ public class UserServiceImpl implements UserService { System.out.println(dto); MemberChangeDto memberChangeDto = new MemberChangeDto(user.getId(), str); - System.out.println(memberChangeDto); userRepository.updatePassword(memberChangeDto.getId(), memberChangeDto.getPassword()); - System.out.println(); return dto; } @@ -115,8 +113,7 @@ public class UserServiceImpl implements UserService { if (user == null) { - System.out.println("불가"); - throw new RuntimeException("유효하지 않은 아이디입니다. 다시 입력하세요"); + throw new UserException("유효하지 않은 아이디입니다. 다시 입력하세요"); } else { diff --git a/backend/src/test/java/com/edufocus/edufocus/user/UserControllerTest.java b/backend/src/test/java/com/edufocus/edufocus/user/UserControllerTest.java deleted file mode 100644 index eef3a64..0000000 --- a/backend/src/test/java/com/edufocus/edufocus/user/UserControllerTest.java +++ /dev/null @@ -1,79 +0,0 @@ -//package com.edufocus.edufocus.user; -// -//import com.edufocus.edufocus.user.controller.UserController; -//import com.edufocus.edufocus.user.model.entity.InfoDto; -//import com.edufocus.edufocus.user.model.entity.PasswordDto; -//import com.edufocus.edufocus.user.model.entity.User; -//import com.edufocus.edufocus.user.model.entity.UserRole; -//import com.edufocus.edufocus.user.model.service.UserService; -//import com.edufocus.edufocus.user.util.JWTUtil; -//import com.fasterxml.jackson.databind.ObjectMapper; -//import org.junit.jupiter.api.BeforeEach; -//import org.junit.jupiter.api.Test; -//import org.junit.jupiter.api.extension.ExtendWith; -//import org.mockito.InjectMocks; -//import org.mockito.Mock; -//import org.mockito.MockitoAnnotations; -//import org.mockito.junit.jupiter.MockitoExtension; -//import org.springframework.beans.factory.annotation.Autowired; -//import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; -//import org.springframework.http.HttpStatus; -//import org.springframework.http.MediaType; -//import org.springframework.http.ResponseEntity; -//import org.springframework.test.web.servlet.MockMvc; -// -//import static org.mockito.ArgumentMatchers.any; -//import static org.mockito.Mockito.*; -//import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; -// -//@WebMvcTest(UserController.class) -//public class UserControllerTest { -// -// @Mock -// private MockMvc mockMvc; -// -// @Autowired -// ObjectMapper mapper; -// @Mock -// private UserService userService; -// -// @Mock -// private JWTUtil jwtUtil; -// -// @InjectMocks -// private UserController userController; -// -// private User testUser; -// private InfoDto testInfo; -// private PasswordDto passwordDto; -// private UserRole userRole; -// @BeforeEach -// void setUp() -// { -// MockitoAnnotations.openMocks(this); -// -// } -//// @Test -//// void testjoin() throws Exception -//// { -//// -//// User user = new User(); -//// user.setId(1L); -//// user.setName("jungmin"); -//// user.setUserId("v_v"); -//// user.setRole(userRole.STUDENT); -//// user.setPassword("pwd"); -//// user.setEmail("wjdals231@naver.com"); -//// -//// String body= mapper.writeValueAsString( -//// UserRe -//// ) -//// -//// mockMvc.perform(post("/api/user/join") -//// .contentType(MediaType.APPLICATION_JSON) -//// . -//// -//// // -//// -//// } -//} diff --git a/backend/src/test/java/com/edufocus/edufocus/user/model/service/UserServiceImplTest.java b/backend/src/test/java/com/edufocus/edufocus/user/model/service/UserServiceImplTest.java index 9fb092b..5cf213f 100644 --- a/backend/src/test/java/com/edufocus/edufocus/user/model/service/UserServiceImplTest.java +++ b/backend/src/test/java/com/edufocus/edufocus/user/model/service/UserServiceImplTest.java @@ -1,19 +1,15 @@ package com.edufocus.edufocus.user.model.service; -import static org.junit.jupiter.api.Assertions.*; - - -import com.edufocus.edufocus.user.model.entity.*; +import com.edufocus.edufocus.user.model.entity.PasswordDto; +import com.edufocus.edufocus.user.model.entity.User; +import com.edufocus.edufocus.user.model.entity.UserRole; import com.edufocus.edufocus.user.model.exception.UserException; import com.edufocus.edufocus.user.model.repository.UserRepository; -import jakarta.transaction.Transactional; -import lombok.RequiredArgsConstructor; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.*; -import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.mail.SimpleMailMessage; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; import org.springframework.mail.javamail.JavaMailSender; import java.util.Optional; @@ -21,7 +17,6 @@ import java.util.Optional; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; -@ExtendWith(MockitoExtension.class) public class UserServiceImplTest { @InjectMocks @@ -30,19 +25,22 @@ public class UserServiceImplTest { @Mock private UserRepository userRepository; - @Mock - private JavaMailSender mailSender; + @BeforeEach - void setUp() { + public void setup() { MockitoAnnotations.openMocks(this); } @Test - void testJoin() { + public void testJoin() { User user = new User(); + user.setUserId("testUser"); + user.setPassword("password"); + user.setEmail("test@example.com"); + user.setRole(UserRole.STUDENT); user.setId(1L); - user.setName("John Doe"); + userService.join(user); @@ -50,249 +48,138 @@ public class UserServiceImplTest { } @Test - void testLoginSuccess() throws Exception { + public void testLogin_Success() throws Exception { User user = new User(); - user.setUserId("john"); - user.setPassword("password"); - - User returnedUser = new User(); - returnedUser.setUserId("john"); - returnedUser.setPassword("password"); - - when(userRepository.findByUserId(user.getUserId())).thenReturn(Optional.of(returnedUser)); - - User result = userService.login(user); - - assertNotNull(result); - assertEquals("john", result.getUserId()); - assertEquals("password", result.getPassword()); - } - - @Test - void testLoginUserNotFound() { - User user = new User(); - user.setUserId("john"); - user.setPassword("password"); - - when(userRepository.findByUserId(user.getUserId())).thenReturn(Optional.empty()); - - UserException exception = assertThrows(UserException.class, () -> userService.login(user)); - - assertEquals("없는 유저", exception.getMessage()); - } - - @Test - void testLoginIncorrectPassword() { - User user = new User(); - user.setUserId("john"); - user.setPassword("password"); - - User returnedUser = new User(); - returnedUser.setUserId("john"); - returnedUser.setPassword("wrongPassword"); - - when(userRepository.findByUserId(user.getUserId())).thenReturn(Optional.of(returnedUser)); - - UserException exception = assertThrows(UserException.class, () -> userService.login(user)); - - assertEquals("비밀번호 틀림", exception.getMessage()); - } - - @Test - void testUserInfo() { - User user = new User(); - user.setId(1L); - user.setName("John Doe"); - - when(userRepository.findById(1L)).thenReturn(Optional.of(user)); - - User result = userService.userInfo(1L); - - assertNotNull(result); - assertEquals("John Doe", result.getName()); - } - - @Test - void testSendEmail() throws Exception { - User user = new User(); - user.setEmail("test@example.com"); user.setUserId("testUser"); - user.setName("Test User"); + user.setPassword("password"); - MailDto mailDto = new MailDto(); - mailDto.setAddress("test@example.com"); - mailDto.setTitle("Test Title"); - mailDto.setMessage("Test Message"); + when(userRepository.findByUserId("testUser")).thenReturn(Optional.of(user)); - doNothing().when(mailSender).send(any(SimpleMailMessage.class)); - doReturn(mailDto).when(userService).createMailAndChargePassword(user); + User loggedInUser = userService.login(user); - userService.sendEamail(user); - - verify(mailSender, times(1)).send(any(SimpleMailMessage.class)); + assertNotNull(loggedInUser); + assertEquals("testUser", loggedInUser.getUserId()); } @Test - void testUserCheck() throws Exception { + public void testLogin_UserNotFound() { User user = new User(); - user.setId(1L); user.setUserId("testUser"); - user.setEmail("test@example.com"); - user.setName("Test User"); + user.setPassword("password"); - when(userRepository.findById(1L)).thenReturn(Optional.of(user)); + when(userRepository.findByUserId("testUser")).thenReturn(Optional.empty()); - userService.userCheck(1L); - - verify(userRepository, times(1)).findById(1L); - verify(userService, times(1)).sendEamail(user); + assertThrows(UserException.class, () -> { + userService.login(user); + }); } @Test - void testUserCheckInvalidId() { - when(userRepository.findById(1L)).thenReturn(Optional.empty()); - - RuntimeException exception = assertThrows(RuntimeException.class, () -> userService.userCheck(1L)); - - assertEquals("유효하지 않은 아이디입니다. 다시 입력하세요", exception.getMessage()); - } - - @Test - void testChangeUserInfo() throws Exception { - Long userId = 1L; - InfoDto infoDto = new InfoDto(); - infoDto.setName("New Name"); - infoDto.setEmail("new.email@example.com"); - + public void testLogin_InvalidPassword() { User user = new User(); - user.setId(userId); - user.setName("Old Name"); - user.setEmail("old.email@example.com"); + user.setUserId("testUser"); + user.setPassword("password"); - when(userRepository.findById(userId)).thenReturn(Optional.of(user)); + User storedUser = new User(); + storedUser.setUserId("testUser"); + storedUser.setPassword("wrongPassword"); - userService.changeuInfo(infoDto, userId); + when(userRepository.findByUserId("testUser")).thenReturn(Optional.of(storedUser)); - assertEquals("New Name", user.getName()); - assertEquals("new.email@example.com", user.getEmail()); - verify(userRepository, times(1)).save(user); - } - - @Test - void testChangeUserInfoUserNotFound() { - Long userId = 1L; - InfoDto infoDto = new InfoDto(); - infoDto.setName("New Name"); - infoDto.setEmail("new.email@example.com"); - - when(userRepository.findById(userId)).thenReturn(Optional.empty()); - - Exception exception = assertThrows(Exception.class, () -> userService.changeuInfo(infoDto, userId)); - - assertEquals("User not found", exception.getMessage()); - } - - @Test - void testChangePasswordSuccess() throws Exception { - Long userId = 1L; - PasswordDto passwordDto = new PasswordDto(); - passwordDto.setCurrentPassword("oldPassword"); - passwordDto.setNewPassword("newPassword"); - passwordDto.setNewPasswordCheck("newPassword"); - - User user = new User(); - user.setId(userId); - user.setPassword("oldPassword"); - - when(userRepository.findById(userId)).thenReturn(Optional.of(user)); - - userService.changePassword(passwordDto, userId); - - assertEquals("newPassword", user.getPassword()); - verify(userRepository, times(1)).save(user); - } - - @Test - void testChangePasswordUserNotFound() { - Long userId = 1L; - PasswordDto passwordDto = new PasswordDto(); - passwordDto.setCurrentPassword("oldPassword"); - passwordDto.setNewPassword("newPassword"); - passwordDto.setNewPasswordCheck("newPassword"); - - when(userRepository.findById(userId)).thenReturn(Optional.empty()); - - Exception exception = assertThrows(Exception.class, () -> userService.changePassword(passwordDto, userId)); - - assertEquals("User not found", exception.getMessage()); - } - - @Test - void testChangePasswordIncorrectCurrentPassword() { - Long userId = 1L; - PasswordDto passwordDto = new PasswordDto(); - passwordDto.setCurrentPassword("wrongPassword"); - passwordDto.setNewPassword("newPassword"); - passwordDto.setNewPasswordCheck("newPassword"); - - User user = new User(); - user.setId(userId); - user.setPassword("oldPassword"); - - when(userRepository.findById(userId)).thenReturn(Optional.of(user)); - - Exception exception = assertThrows(Exception.class, () -> userService.changePassword(passwordDto, userId)); - - assertEquals("Current password is incorrect", exception.getMessage()); - } - - @Test - void testChangePasswordNewPasswordMismatch() { - Long userId = 1L; - PasswordDto passwordDto = new PasswordDto(); - passwordDto.setCurrentPassword("oldPassword"); - passwordDto.setNewPassword("newPassword"); - passwordDto.setNewPasswordCheck("differentNewPassword"); - - User user = new User(); - user.setId(userId); - user.setPassword("oldPassword"); - - when(userRepository.findById(userId)).thenReturn(Optional.of(user)); - - Exception exception = assertThrows(Exception.class, () -> userService.changePassword(passwordDto, userId)); - - assertEquals("New password confirmation does not match", exception.getMessage()); - } - - @Test - void testSaveRefreshToken() throws Exception { - Long userId = 1L; - String refreshToken = "refresh-token"; - - doNothing().when(userRepository).saveRefreshToken(userId, refreshToken); - - userService.saveRefreshToken(userId, refreshToken); - - verify(userRepository, times(1)).saveRefreshToken(userId, refreshToken); - } - - @Test - void testGetRefreshToken() throws Exception { - Long userId = 1L; - String refreshToken = "refresh-token"; - - when(userRepository.getRefreshToken(userId)).thenReturn(refreshToken); - - String result = userService.getRefreshToken(userId); - - assertEquals(refreshToken, result); + assertThrows(UserException.class, () -> { + userService.login(user); + }); } // @Test -// void testDeleteRefreshToken() throws Exception { -// Long userId = 1L; +// public void testUserInfo_Success() { +// User user = new User(); +// user.setId(1L); +// user.setName("testUser"); // -// doNothing().when(userRepository).delete +// when(userRepository.findById(1L)).thenReturn(Optional.of(user)); +// +// User userInfo = userService.userInfo(1L); +// +// assertNotNull(userInfo); +// assertEquals("testUser", userInfo.getName()); +// } +// +// @Test +// public void testUserInfo_UserNotFound() { +// when(userRepository.findById(1L)).thenReturn(Optional.empty()); +// +// assertThrows(UserException.class, () -> { +// userService.userInfo(1L); +// }); +// } +// +// @Test +// public void testChangePassword_Success() throws Exception { +// User user = new User(); +// user.setId(1L); +// user.setPassword("currentPassword"); +// +// when(userRepository.findById(1L)).thenReturn(Optional.of(user)); +// +// PasswordDto passwordDto = new PasswordDto(); +// passwordDto.setCurrentPassword("currentPassword"); +// passwordDto.setNewPassword("newPassword"); +// passwordDto.setNewPasswordCheck("newPassword"); +// +// userService.changePassword(passwordDto, 1L); +// +// verify(userRepository, times(1)).save(user); +// assertEquals("newPassword", user.getPassword()); +// } +// +// @Test +// public void testChangePassword_UserNotFound() { +// when(userRepository.findById(1L)).thenReturn(Optional.empty()); +// +// PasswordDto passwordDto = new PasswordDto(); +// passwordDto.setCurrentPassword("currentPassword"); +// passwordDto.setNewPassword("newPassword"); +// passwordDto.setNewPasswordCheck("newPassword"); +// +// assertThrows(Exception.class, () -> { +// userService.changePassword(passwordDto, 1L); +// }); +// } +// +// @Test +// public void testChangePassword_InvalidCurrentPassword() { +// User user = new User(); +// user.setId(1L); +// user.setPassword("currentPassword"); +// +// when(userRepository.findById(1L)).thenReturn(Optional.of(user)); +// +// PasswordDto passwordDto = new PasswordDto(); +// passwordDto.setCurrentPassword("wrongPassword"); +// passwordDto.setNewPassword("newPassword"); +// passwordDto.setNewPasswordCheck("newPassword"); +// +// assertThrows(Exception.class, () -> { +// userService.changePassword(passwordDto, 1L); +// }); +// } +// +// @Test +// public void testChangePassword_NewPasswordMismatch() { +// User user = new User(); +// user.setId(1L); +// user.setPassword("currentPassword"); +// +// when(userRepository.findById(1L)).thenReturn(Optional.of(user)); +// +// PasswordDto passwordDto = new PasswordDto(); +// passwordDto.setCurrentPassword("currentPassword"); +// passwordDto.setNewPassword("newPassword"); +// passwordDto.setNewPasswordCheck("mismatchNewPassword"); +// +// assertThrows(Exception.class, () -> { +// userService.changePassword(passwordDto, 1L); +// }); // } } \ No newline at end of file From ce4c6a1c8da137d09ec700f02864c404a4cf0a4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A0=95=EB=AF=BC?= Date: Tue, 30 Jul 2024 17:35:37 +0900 Subject: [PATCH 28/32] =?UTF-8?q?feat:=20refresh=20token=20=EC=98=88?= =?UTF-8?q?=EC=99=B8=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../user/controller/UserController.java | 10 ++ .../exception/ExpriedTokenException.java | 11 ++ .../exception/GlobalExceptionHandler.java | 27 +++++ .../exception/InvalidTokenException.java | 7 ++ .../edufocus/edufocus/user/util/JWTUtil.java | 44 ++++--- .../edufocus/video/controller/Controller.java | 108 ++++++++++++------ 6 files changed, 156 insertions(+), 51 deletions(-) create mode 100644 backend/src/main/java/com/edufocus/edufocus/user/model/exception/ExpriedTokenException.java create mode 100644 backend/src/main/java/com/edufocus/edufocus/user/model/exception/GlobalExceptionHandler.java create mode 100644 backend/src/main/java/com/edufocus/edufocus/user/model/exception/InvalidTokenException.java diff --git a/backend/src/main/java/com/edufocus/edufocus/user/controller/UserController.java b/backend/src/main/java/com/edufocus/edufocus/user/controller/UserController.java index b4bff53..6526a03 100644 --- a/backend/src/main/java/com/edufocus/edufocus/user/controller/UserController.java +++ b/backend/src/main/java/com/edufocus/edufocus/user/controller/UserController.java @@ -3,6 +3,8 @@ package com.edufocus.edufocus.user.controller; import com.edufocus.edufocus.user.model.entity.InfoDto; import com.edufocus.edufocus.user.model.entity.PasswordDto; import com.edufocus.edufocus.user.model.entity.User; +import com.edufocus.edufocus.user.model.exception.ExpriedTokenException; +import com.edufocus.edufocus.user.model.exception.UnAuthorizedException; import com.edufocus.edufocus.user.model.service.UserService; import com.edufocus.edufocus.user.util.JWTUtil; import io.swagger.v3.oas.annotations.Operation; @@ -258,7 +260,15 @@ public class UserController { } + @ExceptionHandler(ExpriedTokenException.class) + public ResponseEntity handleExpiredTokenException(){ + return new ResponseEntity<>(HttpStatus.UNAUTHORIZED); + } + @ExceptionHandler(UnAuthorizedException.class) + public ResponseEntity handleUnauthorizedException(){ + return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + } diff --git a/backend/src/main/java/com/edufocus/edufocus/user/model/exception/ExpriedTokenException.java b/backend/src/main/java/com/edufocus/edufocus/user/model/exception/ExpriedTokenException.java new file mode 100644 index 0000000..e816b32 --- /dev/null +++ b/backend/src/main/java/com/edufocus/edufocus/user/model/exception/ExpriedTokenException.java @@ -0,0 +1,11 @@ +package com.edufocus.edufocus.user.model.exception; + +public class ExpriedTokenException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + public ExpriedTokenException() { + super("계정 권한이 유효하지 않습니다.\n다시 로그인을 하세요."); + } + +} diff --git a/backend/src/main/java/com/edufocus/edufocus/user/model/exception/GlobalExceptionHandler.java b/backend/src/main/java/com/edufocus/edufocus/user/model/exception/GlobalExceptionHandler.java new file mode 100644 index 0000000..526e6ae --- /dev/null +++ b/backend/src/main/java/com/edufocus/edufocus/user/model/exception/GlobalExceptionHandler.java @@ -0,0 +1,27 @@ +package com.edufocus.edufocus.user.model.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; + +@ControllerAdvice +public class GlobalExceptionHandler { + + @ExceptionHandler(UnAuthorizedException.class) + public ResponseEntity handleUnAuthorizedException(UnAuthorizedException e) { + + + + return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST); + } + + + @ExceptionHandler(ExpriedTokenException.class) + public ResponseEntity handleInvalidTokenException(ExpriedTokenException e) { + // 로그 남기기 (선택 사항) + // log.error("Invalid token", e); + + return new ResponseEntity<>(e.getMessage(), HttpStatus.UNAUTHORIZED); + } +} diff --git a/backend/src/main/java/com/edufocus/edufocus/user/model/exception/InvalidTokenException.java b/backend/src/main/java/com/edufocus/edufocus/user/model/exception/InvalidTokenException.java new file mode 100644 index 0000000..74969f3 --- /dev/null +++ b/backend/src/main/java/com/edufocus/edufocus/user/model/exception/InvalidTokenException.java @@ -0,0 +1,7 @@ +package com.edufocus.edufocus.user.model.exception; + +public class InvalidTokenException extends RuntimeException { + public InvalidTokenException() { + super("Token is invalid"); + } +} \ No newline at end of file diff --git a/backend/src/main/java/com/edufocus/edufocus/user/util/JWTUtil.java b/backend/src/main/java/com/edufocus/edufocus/user/util/JWTUtil.java index ead01db..ffce34c 100644 --- a/backend/src/main/java/com/edufocus/edufocus/user/util/JWTUtil.java +++ b/backend/src/main/java/com/edufocus/edufocus/user/util/JWTUtil.java @@ -4,6 +4,8 @@ import java.nio.charset.StandardCharsets; import java.util.Date; import java.util.Map; +import com.edufocus.edufocus.user.model.exception.ExpriedTokenException; +import com.edufocus.edufocus.user.model.exception.InvalidTokenException; import com.edufocus.edufocus.user.model.exception.UnAuthorizedException; import io.jsonwebtoken.*; import org.springframework.beans.factory.annotation.Value; @@ -52,19 +54,27 @@ public class JWTUtil { public boolean checkToken(String token) { try { - Jws claims = Jwts.parserBuilder() - .setSigningKey(generateKey()) - .build() - .parseClaimsJws(token); - log.debug("claims: {}", claims); - return true; - } catch (MalformedJwtException | UnsupportedJwtException | IllegalArgumentException | SignatureException | ExpiredJwtException e) { - log.error("Token validation error: {}", e.getMessage()); - return false; - } catch (Exception e) { - System.out.println(token); - log.error("Unexpected error while validating token: {}", e.getMessage()); - return false; + Jws claims = Jwts.parserBuilder() + .setSigningKey(generateKey()) + .build() + .parseClaimsJws(token); + log.debug("claims: {}", claims); + return true; + } catch (MalformedJwtException | UnsupportedJwtException | IllegalArgumentException | SignatureException e) { + log.error("Token validation error: {}", e.getMessage()); + + return false; + } + catch ( ExpiredJwtException e) + { + throw new ExpriedTokenException(); + + } + catch (Exception e) { + System.out.println(token); + System.out.println(e.getMessage()); + log.error("Unexpected error while validating token: {}", e.getMessage()); + return false; } } @@ -77,7 +87,13 @@ public class JWTUtil { Map value = claims.getBody(); log.info("value : {}", value); return (String) value.get("id"); - } catch (Exception e) { + }catch ( ExpiredJwtException e) + { + System.out.println("expired token"); + throw new ExpriedTokenException(); + + } + catch (Exception e) { log.error("Failed to get user ID from token: {}", e.getMessage()); throw new UnAuthorizedException(); } diff --git a/backend/src/main/java/com/edufocus/edufocus/video/controller/Controller.java b/backend/src/main/java/com/edufocus/edufocus/video/controller/Controller.java index c4fa32c..40b206a 100644 --- a/backend/src/main/java/com/edufocus/edufocus/video/controller/Controller.java +++ b/backend/src/main/java/com/edufocus/edufocus/video/controller/Controller.java @@ -7,6 +7,9 @@ import com.edufocus.edufocus.lecture.entity.LectureDetailResponse; import com.edufocus.edufocus.lecture.service.LectureService; import com.edufocus.edufocus.registration.entity.RegistrationStatus; import com.edufocus.edufocus.registration.service.RegistrationService; +import com.edufocus.edufocus.user.model.entity.User; +import com.edufocus.edufocus.user.model.entity.UserRole; +import com.edufocus.edufocus.user.model.repository.UserRepository; import com.edufocus.edufocus.user.model.service.UserService; import com.edufocus.edufocus.user.util.JWTUtil; import com.edufocus.edufocus.video.service.VideoSertvice; @@ -33,6 +36,7 @@ public class Controller { private final LectureService lectureService; private final VideoSertvice videoSertvice; private final RegistrationService registrationService; + private final UserRepository userRepository; @Value("${livekit.api.key}") private String LIVEKIT_API_KEY; @@ -70,39 +74,13 @@ public class Controller { Long userId = Long.parseLong(jwtUtil.getUserId(userToken)); - LectureDetailResponse lecture= lectureService.findLectureById(userId,id); - - String roomName = lecture.getTitle(); - String participantName = userService.getUserName(userId); + User findUser= userRepository.findById(userId).orElse(null); - AccessToken token = new AccessToken(LIVEKIT_API_KEY, LIVEKIT_API_SECRET); - token.setName(participantName); - token.setIdentity(participantName); - token.addGrants(new RoomJoin(true), new RoomName(roomName),new RoomCreate(true)); - - videoSertvice.startOnline(userId,id); + if(findUser.getRole()==UserRole.ADMIN) { + LectureDetailResponse lecture = lectureService.findLectureById(userId, id); - - - return ResponseEntity.ok(Map.of("token", token.toJwt())); - } - - - @PostMapping(value = "/joinroom/{lecture_id}") - public ResponseEntity> joinRoom(@PathVariable("lecture_id") Long id, HttpServletRequest request) throws Exception { - - String userToken = request.getHeader("Authorization"); - - Long userId = Long.parseLong(jwtUtil.getUserId(userToken)); - LectureDetailResponse lecture= lectureService.findLectureById(userId,id); - - - //RegistrationStatus registrationStatus = registrationService.isOnline(userId,id); - -// if(registrationStatus==RegistrationStatus.ACCEPTED) -// { String roomName = lecture.getTitle(); String participantName = userService.getUserName(userId); @@ -110,23 +88,79 @@ public class Controller { AccessToken token = new AccessToken(LIVEKIT_API_KEY, LIVEKIT_API_SECRET); token.setName(participantName); token.setIdentity(participantName); - token.addGrants(new RoomJoin(true), new RoomName(roomName)); - - //videoSertvice.startOnline(userId,id); + token.addGrants(new RoomJoin(true), new RoomName(roomName), new RoomCreate(true)); + videoSertvice.startOnline(userId, id); return ResponseEntity.ok(Map.of("token", token.toJwt())); -// } -// else{ -// return ResponseEntity.status(HttpStatus.FORBIDDEN).body(Map.of("errorMessage", "Not accepted")); -// -// } + + } + else if(findUser.getRole()==UserRole.STUDENT) + { + LectureDetailResponse lecture = lectureService.findLectureById(userId, id); + + + String roomName = lecture.getTitle(); + String participantName = userService.getUserName(userId); + + + AccessToken token = new AccessToken(LIVEKIT_API_KEY, LIVEKIT_API_SECRET); + token.setName(participantName); + token.setIdentity(participantName); + token.addGrants(new RoomJoin(true), new RoomName(roomName), new RoomCreate(true)); + + videoSertvice.startOnline(userId, id); + + + return ResponseEntity.ok(Map.of("token", token.toJwt())); + } + + + return ResponseEntity.ok(Map.of("token", null)); } + +// @PostMapping(value = "/joinroom/{lecture_id}") +// public ResponseEntity> joinRoom(@PathVariable("lecture_id") Long id, HttpServletRequest request) throws Exception { +// +//// String userToken = request.getHeader("Authorization"); +//// +//// Long userId = Long.parseLong(jwtUtil.getUserId(userToken)); +//// LectureDetailResponse lecture= lectureService.findLectureById(userId,id); +//// +//// +//// //RegistrationStatus registrationStatus = registrationService.isOnline(userId,id); +//// +////// if(registrationStatus==RegistrationStatus.ACCEPTED) +////// { +//// String roomName = lecture.getTitle(); +//// String participantName = userService.getUserName(userId); +//// +//// +//// AccessToken token = new AccessToken(LIVEKIT_API_KEY, LIVEKIT_API_SECRET); +//// token.setName(participantName); +//// token.setIdentity(participantName); +//// token.addGrants(new RoomJoin(true), new RoomName(roomName)); +//// +//// //videoSertvice.startOnline(userId,id); +//// +//// +//// +//// return ResponseEntity.ok(Map.of("token", token.toJwt())); +////// } +////// else{ +////// return ResponseEntity.status(HttpStatus.FORBIDDEN).body(Map.of("errorMessage", "Not accepted")); +////// +////// } +//// +// +// +// } + @PostMapping(value = "/livekit/webhook", consumes = "application/webhook+json") public ResponseEntity receiveWebhook(@RequestHeader("Authorization") String authHeader, @RequestBody String body) { WebhookReceiver webhookReceiver = new WebhookReceiver(LIVEKIT_API_KEY, LIVEKIT_API_SECRET); From f813e55733486d0617470d3be51afe8af2173eb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A0=95=EB=AF=BC?= Date: Wed, 31 Jul 2024 10:37:40 +0900 Subject: [PATCH 29/32] BE/userinfo --- .../lecture/service/LectureService.java | 1 + .../lecture/service/LectureServiceImpl.java | 8 ++ .../exception/GlobalExceptionHandler.java | 2 - .../edufocus/video/controller/Controller.java | 103 ++++++++++-------- 4 files changed, 67 insertions(+), 47 deletions(-) diff --git a/backend/src/main/java/com/edufocus/edufocus/lecture/service/LectureService.java b/backend/src/main/java/com/edufocus/edufocus/lecture/service/LectureService.java index 65153a0..e8483d4 100644 --- a/backend/src/main/java/com/edufocus/edufocus/lecture/service/LectureService.java +++ b/backend/src/main/java/com/edufocus/edufocus/lecture/service/LectureService.java @@ -28,4 +28,5 @@ public interface LectureService { Lecture findLectureByTitle(String title); void changeState(Long lectureId); + boolean getState(Long lectureId); } diff --git a/backend/src/main/java/com/edufocus/edufocus/lecture/service/LectureServiceImpl.java b/backend/src/main/java/com/edufocus/edufocus/lecture/service/LectureServiceImpl.java index 90da51c..e770f55 100644 --- a/backend/src/main/java/com/edufocus/edufocus/lecture/service/LectureServiceImpl.java +++ b/backend/src/main/java/com/edufocus/edufocus/lecture/service/LectureServiceImpl.java @@ -239,5 +239,13 @@ public class LectureServiceImpl implements LectureService { lectureRepository.save(l); } + @Override + public boolean getState(Long lectureId) { + + Lecture lecture= lectureRepository.findById(lectureId).orElse(null); + return lecture.isOnline(); + + } + } diff --git a/backend/src/main/java/com/edufocus/edufocus/user/model/exception/GlobalExceptionHandler.java b/backend/src/main/java/com/edufocus/edufocus/user/model/exception/GlobalExceptionHandler.java index 526e6ae..81042dc 100644 --- a/backend/src/main/java/com/edufocus/edufocus/user/model/exception/GlobalExceptionHandler.java +++ b/backend/src/main/java/com/edufocus/edufocus/user/model/exception/GlobalExceptionHandler.java @@ -19,8 +19,6 @@ public class GlobalExceptionHandler { @ExceptionHandler(ExpriedTokenException.class) public ResponseEntity handleInvalidTokenException(ExpriedTokenException e) { - // 로그 남기기 (선택 사항) - // log.error("Invalid token", e); return new ResponseEntity<>(e.getMessage(), HttpStatus.UNAUTHORIZED); } diff --git a/backend/src/main/java/com/edufocus/edufocus/video/controller/Controller.java b/backend/src/main/java/com/edufocus/edufocus/video/controller/Controller.java index 40b206a..1fe7492 100644 --- a/backend/src/main/java/com/edufocus/edufocus/video/controller/Controller.java +++ b/backend/src/main/java/com/edufocus/edufocus/video/controller/Controller.java @@ -4,6 +4,7 @@ import java.util.Map; import com.edufocus.edufocus.lecture.entity.Lecture; import com.edufocus.edufocus.lecture.entity.LectureDetailResponse; +import com.edufocus.edufocus.lecture.repository.LectureRepository; import com.edufocus.edufocus.lecture.service.LectureService; import com.edufocus.edufocus.registration.entity.RegistrationStatus; import com.edufocus.edufocus.registration.service.RegistrationService; @@ -37,6 +38,7 @@ public class Controller { private final VideoSertvice videoSertvice; private final RegistrationService registrationService; private final UserRepository userRepository; + private final LectureRepository lectureRepository; @Value("${livekit.api.key}") private String LIVEKIT_API_KEY; @@ -67,18 +69,37 @@ public class Controller { // } @PostMapping(value = "/makeroom/{lecture_id}") - public ResponseEntity> startLecture(@PathVariable("lecture_id") Long id, HttpServletRequest request) throws Exception { + public ResponseEntity startLecture(@PathVariable("lecture_id") Long id, HttpServletRequest request) throws Exception { + + + String userToken = request.getHeader("Authorization"); + Long userId = Long.parseLong(jwtUtil.getUserId(userToken)); + + + videoSertvice.startOnline(userId, id); + + return new ResponseEntity<>("방만들기 성공",HttpStatus.OK); + + } + + + @PostMapping(value = "/joinroom/{lecture_id}") + public ResponseEntity> joinRoom(@PathVariable("lecture_id") Long id, HttpServletRequest request) throws Exception + { String userToken = request.getHeader("Authorization"); - log.info("userToekn : ", userToken); Long userId = Long.parseLong(jwtUtil.getUserId(userToken)); User findUser= userRepository.findById(userId).orElse(null); + Lecture lecture= lectureRepository.findById(id).orElse(null); - if(findUser.getRole()==UserRole.ADMIN) { - LectureDetailResponse lecture = lectureService.findLectureById(userId, id); + + + + if(findUser.getRole()==UserRole.ADMIN && lecture.isOnline() ) { + String roomName = lecture.getTitle(); @@ -96,9 +117,8 @@ public class Controller { return ResponseEntity.ok(Map.of("token", token.toJwt())); } - else if(findUser.getRole()==UserRole.STUDENT) + else if(findUser.getRole()==UserRole.STUDENT && lecture.isOnline() ) { - LectureDetailResponse lecture = lectureService.findLectureById(userId, id); String roomName = lecture.getTitle(); @@ -108,7 +128,8 @@ public class Controller { AccessToken token = new AccessToken(LIVEKIT_API_KEY, LIVEKIT_API_SECRET); token.setName(participantName); token.setIdentity(participantName); - token.addGrants(new RoomJoin(true), new RoomName(roomName), new RoomCreate(true)); + token.addGrants(new RoomJoin(true), new RoomName(roomName)); + videoSertvice.startOnline(userId, id); @@ -119,48 +140,40 @@ public class Controller { return ResponseEntity.ok(Map.of("token", null)); +// String userToken = request.getHeader("Authorization"); +// +// Long userId = Long.parseLong(jwtUtil.getUserId(userToken)); +// LectureDetailResponse lecture= lectureService.findLectureById(userId,id); +// +// +// //RegistrationStatus registrationStatus = registrationService.isOnline(userId,id); +// +// if(registrationStatus==RegistrationStatus.ACCEPTED) +// { +// String roomName = lecture.getTitle(); +// String participantName = userService.getUserName(userId); +// +// +// AccessToken token = new AccessToken(LIVEKIT_API_KEY, LIVEKIT_API_SECRET); +// token.setName(participantName); +// token.setIdentity(participantName); +// token.addGrants(new RoomJoin(true), new RoomName(roomName)); +// +// //videoSertvice.startOnline(userId,id); +// +// +// +// return ResponseEntity.ok(Map.of("token", token.toJwt())); +// } +// else{ +// return ResponseEntity.status(HttpStatus.FORBIDDEN).body(Map.of("errorMessage", "Not accepted")); +// +// } + } - -// @PostMapping(value = "/joinroom/{lecture_id}") -// public ResponseEntity> joinRoom(@PathVariable("lecture_id") Long id, HttpServletRequest request) throws Exception { -// -//// String userToken = request.getHeader("Authorization"); -//// -//// Long userId = Long.parseLong(jwtUtil.getUserId(userToken)); -//// LectureDetailResponse lecture= lectureService.findLectureById(userId,id); -//// -//// -//// //RegistrationStatus registrationStatus = registrationService.isOnline(userId,id); -//// -////// if(registrationStatus==RegistrationStatus.ACCEPTED) -////// { -//// String roomName = lecture.getTitle(); -//// String participantName = userService.getUserName(userId); -//// -//// -//// AccessToken token = new AccessToken(LIVEKIT_API_KEY, LIVEKIT_API_SECRET); -//// token.setName(participantName); -//// token.setIdentity(participantName); -//// token.addGrants(new RoomJoin(true), new RoomName(roomName)); -//// -//// //videoSertvice.startOnline(userId,id); -//// -//// -//// -//// return ResponseEntity.ok(Map.of("token", token.toJwt())); -////// } -////// else{ -////// return ResponseEntity.status(HttpStatus.FORBIDDEN).body(Map.of("errorMessage", "Not accepted")); -////// -////// } -//// -// -// -// } - @PostMapping(value = "/livekit/webhook", consumes = "application/webhook+json") public ResponseEntity receiveWebhook(@RequestHeader("Authorization") String authHeader, @RequestBody String body) { WebhookReceiver webhookReceiver = new WebhookReceiver(LIVEKIT_API_KEY, LIVEKIT_API_SECRET); From 62dde35a66e1287426f69bf91f20fce4a6a34ccc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A0=95=EB=AF=BC?= Date: Wed, 31 Jul 2024 11:07:11 +0900 Subject: [PATCH 30/32] =?UTF-8?q?feat:=20global=20exception=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../edufocus/user/model/exception/GlobalExceptionHandler.java | 4 ++-- .../main/java/com/edufocus/edufocus/user/util/JWTUtil.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/src/main/java/com/edufocus/edufocus/user/model/exception/GlobalExceptionHandler.java b/backend/src/main/java/com/edufocus/edufocus/user/model/exception/GlobalExceptionHandler.java index 81042dc..63355d3 100644 --- a/backend/src/main/java/com/edufocus/edufocus/user/model/exception/GlobalExceptionHandler.java +++ b/backend/src/main/java/com/edufocus/edufocus/user/model/exception/GlobalExceptionHandler.java @@ -8,8 +8,8 @@ import org.springframework.web.bind.annotation.ExceptionHandler; @ControllerAdvice public class GlobalExceptionHandler { - @ExceptionHandler(UnAuthorizedException.class) - public ResponseEntity handleUnAuthorizedException(UnAuthorizedException e) { + @ExceptionHandler(InvalidTokenException.class) + public ResponseEntity handleUnAuthorizedException(InvalidTokenException e) { diff --git a/backend/src/main/java/com/edufocus/edufocus/user/util/JWTUtil.java b/backend/src/main/java/com/edufocus/edufocus/user/util/JWTUtil.java index ffce34c..89f068f 100644 --- a/backend/src/main/java/com/edufocus/edufocus/user/util/JWTUtil.java +++ b/backend/src/main/java/com/edufocus/edufocus/user/util/JWTUtil.java @@ -95,7 +95,7 @@ public class JWTUtil { } catch (Exception e) { log.error("Failed to get user ID from token: {}", e.getMessage()); - throw new UnAuthorizedException(); + throw new InvalidTokenException(); } } } From 34050e912d986a5a8059b2bb61fad9b7da40684c Mon Sep 17 00:00:00 2001 From: kgc9007 Date: Wed, 31 Jul 2024 11:17:46 +0900 Subject: [PATCH 31/32] =?UTF-8?q?feat:=20Registration=20=EA=B0=95=EC=9D=98?= =?UTF-8?q?=EB=B3=84=20=EC=88=98=EA=B0=95=EC=8B=A0=EC=B2=AD=20=ED=98=84?= =?UTF-8?q?=ED=99=A9=20=EC=A1=B0=ED=9A=8C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lecture/service/LectureService.java | 2 + .../lecture/service/LectureServiceImpl.java | 11 ++-- .../controller/RegistrationController.java | 29 +++++++---- .../registration/entity/Registration.java | 2 +- .../entity/RegistrationSearchResponse.java | 18 +++++++ .../repository/RegistrationRepository.java | 3 ++ .../service/RegistrationService.java | 13 +++-- .../service/RegistrationServiceImpl.java | 51 ++++++++++++++----- 8 files changed, 99 insertions(+), 30 deletions(-) create mode 100644 backend/src/main/java/com/edufocus/edufocus/registration/entity/RegistrationSearchResponse.java diff --git a/backend/src/main/java/com/edufocus/edufocus/lecture/service/LectureService.java b/backend/src/main/java/com/edufocus/edufocus/lecture/service/LectureService.java index 65153a0..50e3b3c 100644 --- a/backend/src/main/java/com/edufocus/edufocus/lecture/service/LectureService.java +++ b/backend/src/main/java/com/edufocus/edufocus/lecture/service/LectureService.java @@ -28,4 +28,6 @@ public interface LectureService { Lecture findLectureByTitle(String title); void changeState(Long lectureId); + + boolean checkTeacher(Long userId, Long lectureId); } diff --git a/backend/src/main/java/com/edufocus/edufocus/lecture/service/LectureServiceImpl.java b/backend/src/main/java/com/edufocus/edufocus/lecture/service/LectureServiceImpl.java index 90da51c..2ba7032 100644 --- a/backend/src/main/java/com/edufocus/edufocus/lecture/service/LectureServiceImpl.java +++ b/backend/src/main/java/com/edufocus/edufocus/lecture/service/LectureServiceImpl.java @@ -223,8 +223,7 @@ public class LectureServiceImpl implements LectureService { Optional lecture = lectureRepository.findById(id); Lecture l; - if(lecture.isPresent()) - { + if (lecture.isPresent()) { l = lecture.get(); System.out.println(l.isOnline()); @@ -232,12 +231,18 @@ public class LectureServiceImpl implements LectureService { System.out.println(l.isOnline()); - }else { + } else { throw new RuntimeException("Lecture not found with id: " + id); } lectureRepository.save(l); } + @Override + public boolean checkTeacher(Long userId, Long lectureId) { + Optional lecture = lectureRepository.findById(lectureId); + + return lecture.isPresent() && lecture.get().getUser().getId() == userId; + } } diff --git a/backend/src/main/java/com/edufocus/edufocus/registration/controller/RegistrationController.java b/backend/src/main/java/com/edufocus/edufocus/registration/controller/RegistrationController.java index ee36efb..6e008fe 100644 --- a/backend/src/main/java/com/edufocus/edufocus/registration/controller/RegistrationController.java +++ b/backend/src/main/java/com/edufocus/edufocus/registration/controller/RegistrationController.java @@ -1,5 +1,6 @@ package com.edufocus.edufocus.registration.controller; +import com.edufocus.edufocus.lecture.service.LectureService; import com.edufocus.edufocus.registration.service.RegistrationService; import com.edufocus.edufocus.user.util.JWTUtil; import lombok.RequiredArgsConstructor; @@ -16,7 +17,9 @@ import java.util.Map; @RequiredArgsConstructor public class RegistrationController { - private final RegistrationService registrationServiceImpl; + private final RegistrationService registrationService; + + private final LectureService lectureService; private final JWTUtil jwtUtil; @@ -25,7 +28,7 @@ public class RegistrationController { Long userId = Long.parseLong(jwtUtil.getUserId(accessToken)); Long lectureId = map.get("lectureId"); - if (!registrationServiceImpl.createRegistration(userId, lectureId)) { + if (!registrationService.createRegistration(userId, lectureId)) { return new ResponseEntity<>(HttpStatus.CONFLICT); } @@ -36,12 +39,10 @@ public class RegistrationController { public ResponseEntity acceptRigistration(@RequestHeader("Authorization") String accessToken, @PathVariable long registrationId) { Long userId = Long.parseLong(jwtUtil.getUserId(accessToken)); - if (!registrationServiceImpl.acceptRegistration(userId, registrationId)) { - String msg = new String("Not Acceptable"); - return new ResponseEntity<>(HttpStatus.UNAUTHORIZED); + if (!registrationService.acceptRegistration(userId, registrationId)) { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); } - String msg = new String("registration accepted"); return new ResponseEntity<>(HttpStatus.OK); } @@ -49,12 +50,22 @@ public class RegistrationController { public ResponseEntity deleteRigistration(@RequestHeader("Authorization") String accessToken, @PathVariable long registrationId) { Long userId = Long.parseLong(jwtUtil.getUserId(accessToken)); - if (!registrationServiceImpl.deleteRegistration(userId, registrationId)) { - String msg = new String("Not Acceptable"); - return new ResponseEntity<>(HttpStatus.UNAUTHORIZED); + if (!registrationService.deleteRegistration(userId, registrationId)) { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); } return new ResponseEntity<>(HttpStatus.NO_CONTENT); } + @GetMapping("/{lectureId}") + public ResponseEntity getRegistrations(@RequestHeader("Authorization") String accessToken, @PathVariable long lectureId) { + Long userId = Long.parseLong(jwtUtil.getUserId(accessToken)); + + if (!lectureService.checkTeacher(userId, lectureId)) { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + + return new ResponseEntity<>(registrationService.searchRegistrations(lectureId), HttpStatus.OK); + } + } diff --git a/backend/src/main/java/com/edufocus/edufocus/registration/entity/Registration.java b/backend/src/main/java/com/edufocus/edufocus/registration/entity/Registration.java index 4a81aba..38cfe35 100644 --- a/backend/src/main/java/com/edufocus/edufocus/registration/entity/Registration.java +++ b/backend/src/main/java/com/edufocus/edufocus/registration/entity/Registration.java @@ -26,5 +26,5 @@ public class Registration { private Lecture lecture; @Enumerated(EnumType.STRING) - private RegistrationStatus status ; + private RegistrationStatus status; } diff --git a/backend/src/main/java/com/edufocus/edufocus/registration/entity/RegistrationSearchResponse.java b/backend/src/main/java/com/edufocus/edufocus/registration/entity/RegistrationSearchResponse.java new file mode 100644 index 0000000..e9c1b46 --- /dev/null +++ b/backend/src/main/java/com/edufocus/edufocus/registration/entity/RegistrationSearchResponse.java @@ -0,0 +1,18 @@ +package com.edufocus.edufocus.registration.entity; + +import com.edufocus.edufocus.lecture.entity.Lecture; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class RegistrationSearchResponse { + + private Long id; + + private String userName; +} diff --git a/backend/src/main/java/com/edufocus/edufocus/registration/repository/RegistrationRepository.java b/backend/src/main/java/com/edufocus/edufocus/registration/repository/RegistrationRepository.java index 251acaf..f8c31b7 100644 --- a/backend/src/main/java/com/edufocus/edufocus/registration/repository/RegistrationRepository.java +++ b/backend/src/main/java/com/edufocus/edufocus/registration/repository/RegistrationRepository.java @@ -2,6 +2,7 @@ package com.edufocus.edufocus.registration.repository; import com.edufocus.edufocus.registration.entity.Registration; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; @@ -11,5 +12,7 @@ import java.util.List; public interface RegistrationRepository extends JpaRepository { List findAllByUserId(@Param("userId") Long userId); + List findAllNotAcceptedByLectureId(@Param("lectureId") Long lectureId); + Registration findByUserIdAndLectureId(Long userId, Long lectureId); } diff --git a/backend/src/main/java/com/edufocus/edufocus/registration/service/RegistrationService.java b/backend/src/main/java/com/edufocus/edufocus/registration/service/RegistrationService.java index bc27b37..3c7d477 100644 --- a/backend/src/main/java/com/edufocus/edufocus/registration/service/RegistrationService.java +++ b/backend/src/main/java/com/edufocus/edufocus/registration/service/RegistrationService.java @@ -1,17 +1,22 @@ package com.edufocus.edufocus.registration.service; import com.edufocus.edufocus.registration.entity.Registration; +import com.edufocus.edufocus.registration.entity.RegistrationSearchResponse; import com.edufocus.edufocus.registration.entity.RegistrationStatus; import org.springframework.stereotype.Service; +import java.util.List; + @Service public interface RegistrationService { - boolean createRegistration(long userId, long registrationId); + boolean createRegistration(Long userId, Long registrationId); - boolean acceptRegistration(long userId, long RegistrationId); + boolean acceptRegistration(Long userId, Long RegistrationId); - boolean deleteRegistration(long userId, long registrationId); + boolean deleteRegistration(Long userId, Long registrationId); - RegistrationStatus isOnline(Long userId , Long lectureId); + List searchRegistrations(Long LectureId); + + RegistrationStatus isOnline(Long userId, Long lectureId); } diff --git a/backend/src/main/java/com/edufocus/edufocus/registration/service/RegistrationServiceImpl.java b/backend/src/main/java/com/edufocus/edufocus/registration/service/RegistrationServiceImpl.java index 0eca5b5..e36c9a1 100644 --- a/backend/src/main/java/com/edufocus/edufocus/registration/service/RegistrationServiceImpl.java +++ b/backend/src/main/java/com/edufocus/edufocus/registration/service/RegistrationServiceImpl.java @@ -1,14 +1,20 @@ package com.edufocus.edufocus.registration.service; +import com.edufocus.edufocus.lecture.entity.Lecture; import com.edufocus.edufocus.lecture.repository.LectureRepository; import com.edufocus.edufocus.registration.entity.Registration; +import com.edufocus.edufocus.registration.entity.RegistrationSearchResponse; import com.edufocus.edufocus.registration.entity.RegistrationStatus; import com.edufocus.edufocus.registration.repository.RegistrationRepository; +import com.edufocus.edufocus.user.model.entity.User; +import com.edufocus.edufocus.user.model.entity.UserRole; import com.edufocus.edufocus.user.model.repository.UserRepository; import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import java.util.ArrayList; +import java.util.List; import java.util.Optional; @Service @@ -21,14 +27,16 @@ public class RegistrationServiceImpl implements RegistrationService { private final LectureRepository lectureRepository; @Override - public boolean createRegistration(long userId, long lectureId) { - if (registrationRepository.findByUserIdAndLectureId(userId, lectureId) != null) { + public boolean createRegistration(Long userId, Long lectureId) { + Optional user = userRepository.findById(userId); + + if (user.isEmpty() || user.get().getRole().equals(UserRole.ADMIN)) { return false; } Registration registration = new Registration().builder() - .user(userRepository.getReferenceById(userId)) - .lecture(lectureRepository.getReferenceById(lectureId)) + .user(userRepository.findById(userId).get()) + .lecture(lectureRepository.findById(lectureId).get()) .status(RegistrationStatus.WAITING) .build(); @@ -37,24 +45,24 @@ public class RegistrationServiceImpl implements RegistrationService { } @Override - public boolean acceptRegistration(long userId, long registrationId) { - Registration registration = registrationRepository.findById(registrationId).get(); + public boolean acceptRegistration(Long userId, Long registrationId) { + Optional registration = registrationRepository.findById(registrationId); - if (registration.getLecture().getUser().getId() != userId) { + if (registration.isEmpty() || registration.get().getLecture().getUser().getId() != userId) { return false; } - registration.setStatus(RegistrationStatus.valueOf("ACCEPTED")); - registrationRepository.save(registration); + registration.get().setStatus(RegistrationStatus.valueOf("ACCEPTED")); + registrationRepository.save(registration.get()); return true; } @Override - public boolean deleteRegistration(long userId, long registrationId) { - Registration registration = registrationRepository.findById(registrationId).get(); + public boolean deleteRegistration(Long userId, Long registrationId) { + Optional registration = registrationRepository.findById(registrationId); - if (registration.getLecture().getUser().getId() != userId) { + if (registration.isEmpty() || registration.get().getLecture().getUser().getId() != userId) { return false; } @@ -62,10 +70,27 @@ public class RegistrationServiceImpl implements RegistrationService { return true; } + @Override + public List searchRegistrations(Long lectureId) { + List registrations = registrationRepository.findAllNotAcceptedByLectureId(lectureId); + + List responses = new ArrayList<>(); + for (Registration registration : registrations) { + RegistrationSearchResponse response = new RegistrationSearchResponse().builder() + .id(registration.getId()) + .userName(registration.getUser().getName()) + .build(); + + responses.add(response); + } + + return responses; + } + @Override public RegistrationStatus isOnline(Long userId, Long lectureId) { - Registration registration = registrationRepository.findByUserIdAndLectureId(userId,lectureId); + Registration registration = registrationRepository.findByUserIdAndLectureId(userId, lectureId); return registration.getStatus(); } From 60baa59027bc31c4bd5126da143796f73ac0bf48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A0=95=EB=AF=BC?= Date: Wed, 31 Jul 2024 12:37:37 +0900 Subject: [PATCH 32/32] =?UTF-8?q?feat:=20openvidu=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/edufocus/edufocus/video/controller/Controller.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/main/java/com/edufocus/edufocus/video/controller/Controller.java b/backend/src/main/java/com/edufocus/edufocus/video/controller/Controller.java index 1fe7492..4c470ed 100644 --- a/backend/src/main/java/com/edufocus/edufocus/video/controller/Controller.java +++ b/backend/src/main/java/com/edufocus/edufocus/video/controller/Controller.java @@ -98,7 +98,7 @@ public class Controller { - if(findUser.getRole()==UserRole.ADMIN && lecture.isOnline() ) { + if(findUser.getRole()==UserRole.ADMIN ){//&& lecture.isOnline() ) { @@ -117,7 +117,7 @@ public class Controller { return ResponseEntity.ok(Map.of("token", token.toJwt())); } - else if(findUser.getRole()==UserRole.STUDENT && lecture.isOnline() ) + else if(findUser.getRole()==UserRole.STUDENT )// && lecture.isOnline() ) {