diff --git a/backend/shareNote/src/main/java/com/Backend/shareNote/domain/Oraganization/DTOs/quizdto/QuizCreateDTO.java b/backend/shareNote/src/main/java/com/Backend/shareNote/domain/Oraganization/DTOs/quizdto/QuizCreateDTO.java new file mode 100644 index 00000000..fad15c90 --- /dev/null +++ b/backend/shareNote/src/main/java/com/Backend/shareNote/domain/Oraganization/DTOs/quizdto/QuizCreateDTO.java @@ -0,0 +1,17 @@ +package com.Backend.shareNote.domain.Oraganization.DTOs.quizdto; + +import lombok.Data; + +import java.util.List; + +@Data +public class QuizCreateDTO { + private String organizationId; + private String noteId; + private String userId; + private String quizType; + private String problem; + private Integer answer; + private List problems; + private String quizTitle; +} diff --git a/backend/shareNote/src/main/java/com/Backend/shareNote/domain/Oraganization/DTOs/quizdto/QuizDeleteDTO.java b/backend/shareNote/src/main/java/com/Backend/shareNote/domain/Oraganization/DTOs/quizdto/QuizDeleteDTO.java new file mode 100644 index 00000000..285ae2d7 --- /dev/null +++ b/backend/shareNote/src/main/java/com/Backend/shareNote/domain/Oraganization/DTOs/quizdto/QuizDeleteDTO.java @@ -0,0 +1,11 @@ +package com.Backend.shareNote.domain.Oraganization.DTOs.quizdto; + +import lombok.Data; + +@Data +public class QuizDeleteDTO { + private String organizationId; + private String noteId; + private String userId; + private String quizId; +} diff --git a/backend/shareNote/src/main/java/com/Backend/shareNote/domain/Oraganization/DTOs/quizdto/QuizDetailReqDTO.java b/backend/shareNote/src/main/java/com/Backend/shareNote/domain/Oraganization/DTOs/quizdto/QuizDetailReqDTO.java new file mode 100644 index 00000000..5bb2588c --- /dev/null +++ b/backend/shareNote/src/main/java/com/Backend/shareNote/domain/Oraganization/DTOs/quizdto/QuizDetailReqDTO.java @@ -0,0 +1,11 @@ +package com.Backend.shareNote.domain.Oraganization.DTOs.quizdto; + +import lombok.Data; + +@Data +public class QuizDetailReqDTO { + private String organizationId; + private String noteId; + private String userId; + private String quizId; +} diff --git a/backend/shareNote/src/main/java/com/Backend/shareNote/domain/Oraganization/DTOs/quizdto/QuizDetailResDTO.java b/backend/shareNote/src/main/java/com/Backend/shareNote/domain/Oraganization/DTOs/quizdto/QuizDetailResDTO.java new file mode 100644 index 00000000..13bd1234 --- /dev/null +++ b/backend/shareNote/src/main/java/com/Backend/shareNote/domain/Oraganization/DTOs/quizdto/QuizDetailResDTO.java @@ -0,0 +1,16 @@ +package com.Backend.shareNote.domain.Oraganization.DTOs.quizdto; + +import lombok.Builder; +import lombok.Data; + +import java.util.List; + +@Data +@Builder +public class QuizDetailResDTO { + private String quizTitle; + private String quizType; + private String noteName; // 노트 이름 + private List problems; + private Integer correct; +} diff --git a/backend/shareNote/src/main/java/com/Backend/shareNote/domain/Oraganization/DTOs/quizdto/QuizSearchDTO.java b/backend/shareNote/src/main/java/com/Backend/shareNote/domain/Oraganization/DTOs/quizdto/QuizSearchDTO.java new file mode 100644 index 00000000..bc05a4f9 --- /dev/null +++ b/backend/shareNote/src/main/java/com/Backend/shareNote/domain/Oraganization/DTOs/quizdto/QuizSearchDTO.java @@ -0,0 +1,11 @@ +package com.Backend.shareNote.domain.Oraganization.DTOs.quizdto; + +import lombok.Data; + +@Data +public class QuizSearchDTO { + private String quizId; + private String quizTitle; + private Integer correct; + private String nickname; +} diff --git a/backend/shareNote/src/main/java/com/Backend/shareNote/domain/Oraganization/DTOs/quizdto/QuizSolveDTO.java b/backend/shareNote/src/main/java/com/Backend/shareNote/domain/Oraganization/DTOs/quizdto/QuizSolveDTO.java new file mode 100644 index 00000000..5b858bcc --- /dev/null +++ b/backend/shareNote/src/main/java/com/Backend/shareNote/domain/Oraganization/DTOs/quizdto/QuizSolveDTO.java @@ -0,0 +1,13 @@ +package com.Backend.shareNote.domain.Oraganization.DTOs.quizdto; + +import lombok.Data; + +@Data +public class QuizSolveDTO { + private String organizationId; + private String noteId; + private String userId; + private String quizId; + private Integer answer; + private String nickname; +} diff --git a/backend/shareNote/src/main/java/com/Backend/shareNote/domain/Oraganization/controller/QuizController.java b/backend/shareNote/src/main/java/com/Backend/shareNote/domain/Oraganization/controller/QuizController.java new file mode 100644 index 00000000..77a25676 --- /dev/null +++ b/backend/shareNote/src/main/java/com/Backend/shareNote/domain/Oraganization/controller/QuizController.java @@ -0,0 +1,40 @@ +package com.Backend.shareNote.domain.Oraganization.controller; + +import com.Backend.shareNote.domain.Oraganization.DTOs.quizdto.*; +import com.Backend.shareNote.domain.Oraganization.service.QuizService; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/api") +public class QuizController { + private final QuizService quizService; + @PostMapping("/quiz") + public ResponseEntity createQuiz(@RequestBody QuizCreateDTO quizCreateDTO){ + return quizService.createQuiz(quizCreateDTO); + } + + @PostMapping("/quiz-solutions") + public ResponseEntity createQuizSolutions(@RequestBody QuizSolveDTO quizCreateDTO){ + return quizService.createQuizSolutions(quizCreateDTO); + } + + @GetMapping("/quiz/{organizationId}/{noteId}/{userId}") + public ResponseEntity getQuiz(@PathVariable String organizationId, @PathVariable String noteId, @PathVariable String userId){ + return quizService.getQuiz(organizationId, noteId, userId); + } + + @PostMapping("/quiz/detail") + public ResponseEntity getQuizDetail(@RequestBody QuizDetailReqDTO quizDetailReqDTO){ + return quizService.getQuizDetail(quizDetailReqDTO); + } + + @DeleteMapping("/quiz") + public ResponseEntity deleteQuiz(@RequestBody QuizDeleteDTO quizDeleteDTO){ + return quizService.deleteQuiz(quizDeleteDTO); + } +} diff --git a/backend/shareNote/src/main/java/com/Backend/shareNote/domain/Oraganization/entity/Organization.java b/backend/shareNote/src/main/java/com/Backend/shareNote/domain/Oraganization/entity/Organization.java index 77d25493..1ba9d2f6 100644 --- a/backend/shareNote/src/main/java/com/Backend/shareNote/domain/Oraganization/entity/Organization.java +++ b/backend/shareNote/src/main/java/com/Backend/shareNote/domain/Oraganization/entity/Organization.java @@ -62,6 +62,7 @@ public static class Note { private LocalDateTime createdAt; + private List quiz; // 생성자, 게터, 세터 등 필요한 메서드들 추가 } @@ -79,6 +80,38 @@ public static class Page { private LocalDateTime createdAt; } + + @Getter + @Builder + @Document(collection = "quizs") + public static class Quiz { + @Id + private String id; + // 생성자 + private String userId; + // 객관식 or 주관식 + private String quizType; + // 문제 설명 + private String problem; + // 답안 + private int answer; + // 객관식 보기 + private List problems; + @CreatedDate + private LocalDateTime createdAt; + + // 정답 맞춘 유저 + private List correctUser; + // 정답 틀린 유저 + private List wrongUser; + + // 닉네임 + private String nickname; + private String quizTitle; + + } + + @Getter @Slf4j public static class LikesInfo { diff --git a/backend/shareNote/src/main/java/com/Backend/shareNote/domain/Oraganization/repository/QuizRepository.java b/backend/shareNote/src/main/java/com/Backend/shareNote/domain/Oraganization/repository/QuizRepository.java new file mode 100644 index 00000000..48f00f94 --- /dev/null +++ b/backend/shareNote/src/main/java/com/Backend/shareNote/domain/Oraganization/repository/QuizRepository.java @@ -0,0 +1,9 @@ +package com.Backend.shareNote.domain.Oraganization.repository; + +import com.Backend.shareNote.domain.Oraganization.entity.Organization; +import org.springframework.data.mongodb.repository.MongoRepository; +import org.springframework.stereotype.Repository; +@Repository +public interface QuizRepository extends MongoRepository{ +} + diff --git a/backend/shareNote/src/main/java/com/Backend/shareNote/domain/Oraganization/service/NoteService.java b/backend/shareNote/src/main/java/com/Backend/shareNote/domain/Oraganization/service/NoteService.java index efb655d6..ba5cd173 100644 --- a/backend/shareNote/src/main/java/com/Backend/shareNote/domain/Oraganization/service/NoteService.java +++ b/backend/shareNote/src/main/java/com/Backend/shareNote/domain/Oraganization/service/NoteService.java @@ -37,6 +37,7 @@ public ResponseEntity createNote(NoteCreateDTO noteCreateDTO) { .pages(new ArrayList()) .noteImageUrl(noteCreateDTO.getNoteImageUrl()) .likesInfo(new Organization.LikesInfo()) + .quiz(new ArrayList()) .build(); noteRepository.save(note); // organization에 note 추가 diff --git a/backend/shareNote/src/main/java/com/Backend/shareNote/domain/Oraganization/service/OrganizationService.java b/backend/shareNote/src/main/java/com/Backend/shareNote/domain/Oraganization/service/OrganizationService.java index d00aa591..16d136e7 100644 --- a/backend/shareNote/src/main/java/com/Backend/shareNote/domain/Oraganization/service/OrganizationService.java +++ b/backend/shareNote/src/main/java/com/Backend/shareNote/domain/Oraganization/service/OrganizationService.java @@ -37,7 +37,6 @@ public ResponseEntity createOrganization(OrganizationCrea .owner(organizationCreateDTO.getOwner()) .emoji(organizationCreateDTO.getEmoji()) // 이모지 추가 .notes(new ArrayList()) - .quiz(new ArrayList()) .members(members) .description("") .build(); diff --git a/backend/shareNote/src/main/java/com/Backend/shareNote/domain/Oraganization/service/QuizService.java b/backend/shareNote/src/main/java/com/Backend/shareNote/domain/Oraganization/service/QuizService.java new file mode 100644 index 00000000..714cb087 --- /dev/null +++ b/backend/shareNote/src/main/java/com/Backend/shareNote/domain/Oraganization/service/QuizService.java @@ -0,0 +1,259 @@ +package com.Backend.shareNote.domain.Oraganization.service; + +import com.Backend.shareNote.domain.Oraganization.DTOs.quizdto.*; +import com.Backend.shareNote.domain.Oraganization.entity.Organization; +import com.Backend.shareNote.domain.Oraganization.repository.OrganizationRepository; +import com.Backend.shareNote.domain.Oraganization.repository.QuizRepository; +import com.Backend.shareNote.domain.User.entity.Users; +import com.Backend.shareNote.domain.User.repository.UserRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.List; + +@Service +@RequiredArgsConstructor +public class QuizService { + private final OrganizationRepository organizationRepository; + + private final QuizRepository quizRepository; + private final UserRepository userRepository; + @Transactional + public ResponseEntity createQuiz(QuizCreateDTO quizCreateDTO) { + try { + // organization 찾기 + Organization organization = organizationRepository.findById(quizCreateDTO.getOrganizationId()) + .orElseThrow(() -> new IllegalArgumentException("해당하는 organization이 없습니다.")); + + // note 찾기 + Organization.Note note = organization.getNotes().stream() + .filter(n -> n.getId().equals(quizCreateDTO.getNoteId())) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("해당하는 note가 없습니다.")); + + Users user = userRepository.findById(quizCreateDTO.getUserId()).get(); + // quiz 생성 + Organization.Quiz quiz = Organization.Quiz.builder() + .quizType(quizCreateDTO.getQuizType()) + .problem(quizCreateDTO.getProblem()) + .answer(quizCreateDTO.getAnswer()) + .problems(quizCreateDTO.getProblems()) + .correctUser(new ArrayList()) + .wrongUser(new ArrayList()) + .userId(quizCreateDTO.getUserId()) + .nickname(user.getNickname()) + .quizTitle(quizCreateDTO.getQuizTitle()) + .build(); + + + + // 퀴즈를 note에 추가 + note.getQuiz().add(quiz); + + // organization 멤버인지 확인하는 코드 + if (!organization.getMembers().contains(quizCreateDTO.getUserId())) { + throw new IllegalArgumentException("해당하는 organization의 멤버가 아닙니다."); + } + + quizRepository.save(quiz); + + + + organizationRepository.save(organization); + + return ResponseEntity.ok().body("퀴즈 생성 성공"); + }catch (IllegalArgumentException e) { + return ResponseEntity.badRequest().body("Invalid input: " + e.getMessage()); + } + catch (Exception e) { + return ResponseEntity.badRequest().body("Unexpected error: " + e.getMessage()); + } + } + @Transactional + public ResponseEntity createQuizSolutions(QuizSolveDTO quizCreateDTO) { + try { + // organization 찾기 + Organization organization = organizationRepository.findById(quizCreateDTO.getOrganizationId()) + .orElseThrow(() -> new IllegalArgumentException("해당하는 organization이 없습니다.")); + + // note 찾기 + Organization.Note note = organization.getNotes().stream() + .filter(n -> n.getId().equals(quizCreateDTO.getNoteId())) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("해당하는 note가 없습니다.")); + + // quiz 찾기 + Organization.Quiz quiz = note.getQuiz().stream() + .filter(q -> q.getId().equals(quizCreateDTO.getQuizId())) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("해당하는 quiz가 없습니다.")); + + // organization 멤버인지 확인하는 코드 + if (!organization.getMembers().contains(quizCreateDTO.getUserId())) { + throw new IllegalArgumentException("해당하는 organization의 멤버가 아닙니다."); + } + + // 자기가 낸 문제 푸는 경우 + if (quiz.getUserId().equals(quizCreateDTO.getUserId())) { + throw new IllegalArgumentException("자신이 낸 문제는 풀 수 없습니다."); + } + + // CorrectUser, WrongUser에 userId가 있으면 이미 풀었다는 뜻 + // 이미 푼 문제에 대해서 제출을 시도할 때 + if (quiz.getCorrectUser().contains(quizCreateDTO.getUserId()) || + quiz.getWrongUser().contains(quizCreateDTO.getUserId())) { + throw new IllegalArgumentException("이미 풀었습니다."); + } + + // 정답 확인 + if (quiz.getAnswer() == quizCreateDTO.getAnswer()) { + quiz.getCorrectUser().add(quizCreateDTO.getUserId()); + organizationRepository.save(organization); + return ResponseEntity.ok().body("퀴즈 정답!!"); + } else { + quiz.getWrongUser().add(quizCreateDTO.getUserId()); + organizationRepository.save(organization); + return ResponseEntity.ok().body("퀴즈 실패!! 정답은 " + quiz.getAnswer() + "입니다"); + } + + }catch (IllegalArgumentException e) { + return ResponseEntity.badRequest().body("Invalid input: " + e.getMessage()); + } + catch (Exception e) { + return ResponseEntity.badRequest().body("Unexpected error: " + e.getMessage()); + } + } + + + public ResponseEntity getQuiz(String organizationId, String noteId, String userId) { + try { + // organization 찾기 + Organization organization = organizationRepository.findById(organizationId) + .orElseThrow(() -> new IllegalArgumentException("해당하는 organization이 없습니다.")); + + // note 찾기 + Organization.Note note = organization.getNotes().stream() + .filter(n -> n.getId().equals(noteId)) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("해당하는 note가 없습니다.")); + List quizList = new ArrayList<>(); + + + note.getQuiz().stream().filter(quiz -> !quiz.getUserId().equals(userId)).forEach(quiz -> { + QuizSearchDTO quizSearchDTO = new QuizSearchDTO(); + quizSearchDTO.setQuizId(quiz.getId()); + quizSearchDTO.setQuizTitle(quiz.getProblem()); + quizSearchDTO.setNickname(quiz.getNickname()); + if(quiz.getCorrectUser().contains(userId)){ + quizSearchDTO.setCorrect(1); + } + else if(quiz.getWrongUser().contains(userId)){ + quizSearchDTO.setCorrect(0); + } + else{ + quizSearchDTO.setCorrect(-1); + } + quizList.add(quizSearchDTO); + }); + + // quiz 찾기 + return ResponseEntity.ok().body(quizList); + }catch (IllegalArgumentException e) { + return ResponseEntity.badRequest().body("Invalid input: " + e.getMessage()); + } + catch (Exception e) { + return ResponseEntity.badRequest().body("Unexpected error: " + e.getMessage()); + } + + } + + public ResponseEntity getQuizDetail(QuizDetailReqDTO quizDetailReqDTO) { + try { + // organization 찾기 + Organization organization = organizationRepository.findById(quizDetailReqDTO.getOrganizationId()) + .orElseThrow(() -> new IllegalArgumentException("해당하는 organization이 없습니다.")); + + // note 찾기 + Organization.Note note = organization.getNotes().stream() + .filter(n -> n.getId().equals(quizDetailReqDTO.getNoteId())) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("해당하는 note가 없습니다.")); + + // quiz 찾기 + Organization.Quiz quiz = note.getQuiz().stream() + .filter(q -> q.getId().equals(quizDetailReqDTO.getQuizId())) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("해당하는 quiz가 없습니다.")); + + // + int corret; + if(quiz.getCorrectUser().contains(quizDetailReqDTO.getUserId())){ + corret = 1; + } + else if(quiz.getWrongUser().contains(quizDetailReqDTO.getUserId())){ + corret = 0; + } + else{ + corret = -1; + } + + QuizDetailResDTO dto = QuizDetailResDTO.builder() + .quizTitle(quiz.getProblem()) + .quizType(quiz.getQuizType()) + .noteName(note.getTitle()) + .problems(quiz.getProblems()) + .correct(corret) + .build(); + + return ResponseEntity.ok().body(dto); + }catch (IllegalArgumentException e) { + return ResponseEntity.badRequest().body("Invalid input: " + e.getMessage()); + } + catch (Exception e) { + return ResponseEntity.badRequest().body("Unexpected error: " + e.getMessage()); + } + } + + public ResponseEntity deleteQuiz(QuizDeleteDTO quizDeleteDTO) { + try { + // organization 찾기 + Organization organization = organizationRepository.findById(quizDeleteDTO.getOrganizationId()) + .orElseThrow(() -> new IllegalArgumentException("해당하는 organization이 없습니다.")); + + // note 찾기 + Organization.Note note = organization.getNotes().stream() + .filter(n -> n.getId().equals(quizDeleteDTO.getNoteId())) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("해당하는 note가 없습니다.")); + + // quiz 찾기 + Organization.Quiz quiz = note.getQuiz().stream() + .filter(q -> q.getId().equals(quizDeleteDTO.getQuizId())) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("해당하는 quiz가 없습니다.")); + + // organization 멤버인지 확인하는 코드 + if (!organization.getMembers().contains(quizDeleteDTO.getUserId())) { + throw new IllegalArgumentException("해당하는 organization의 멤버가 아닙니다."); + } + + // 자기가 낸 문제인 경우만 삭제 가능 + if (quiz.getUserId().equals(quizDeleteDTO.getUserId())) { + note.getQuiz().remove(quiz); + organizationRepository.save(organization); + } else{ + throw new IllegalArgumentException("자신이 낸 문제만 삭제 가능합니다."); + } + + return ResponseEntity.ok().body("퀴즈 삭제 성공"); + }catch (IllegalArgumentException e) { + return ResponseEntity.badRequest().body("Invalid input: " + e.getMessage()); + } + catch (Exception e) { + return ResponseEntity.badRequest().body("Unexpected error: " + e.getMessage()); + } + } +} diff --git a/backend/shareNote/src/main/java/com/Backend/shareNote/domain/Quiz/entity/Quiz.java b/backend/shareNote/src/main/java/com/Backend/shareNote/domain/Quiz/entity/Quiz.java deleted file mode 100644 index 24e7f4c7..00000000 --- a/backend/shareNote/src/main/java/com/Backend/shareNote/domain/Quiz/entity/Quiz.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.Backend.shareNote.domain.Quiz.entity; - -import org.springframework.data.annotation.Id; -import org.springframework.data.mongodb.core.mapping.Document; -import org.springframework.data.mongodb.core.mapping.Field; - -import java.util.Date; -import java.util.List; - -@Document(collection = "quizzes") -public class Quiz { - - @Id - private String id; - - @Field("noteId") - private String noteId; - - @Field("ownerId") - private String ownerId; - - @Field("created_at") - private Date createdAt; - - @Field("question_type") - private String questionType; - - private String question; - - private List choices; // 객관식 옵션 - - private String answer; - - @Field("solverUser") - private List solverUsers; - - // 생성자, 게터, 세터 등 필요한 메서드들 추가 -} \ No newline at end of file diff --git a/nodejs/server.js b/nodejs/server.js index b925f55f..f83b0766 100644 --- a/nodejs/server.js +++ b/nodejs/server.js @@ -24,8 +24,8 @@ const databaseName = 'shareDB'; function createConnectionString(databaseName) { logger.info(`@@@@@@@@@@@mongodb://root:1234@localhost:27017/${databaseName}?authSource=admin`); - //return `mongodb://root:1234@localhost:27017/${databaseName}?authSource=admin`; - return `mongodb://root:1234@mongodbService:27017/${databaseName}?authSource=admin`; + return `mongodb://root:1234@localhost:27017/${databaseName}?authSource=admin`; + //return `mongodb://root:1234@mongodbService:27017/${databaseName}?authSource=admin`; } const server = http.createServer((req, res) => { @@ -58,7 +58,7 @@ wss.on('connection', (ws, req) => { }); const mdb = new MongodbPersistence(createConnectionString(databaseName), { - collectionName: 'Pages', + collectionName: 'transactions', flushSize: 100, multipleCollections: true, }); @@ -69,15 +69,7 @@ yUtils.setPersistence({ const persistedYdoc = await mdb.getYDoc(docName); const newUpdates = Y.encodeStateAsUpdate(ydoc); - //작성자의 nickname 추가 - const authorNickname = 'nickname'; - //추가 부분 - const updateWithAuthor = Y.encodeStateAsUpdate({ - ...Y.applyUpdate(Y.emptyUpdate, newUpdates), - author: authorNickname, - }); - - await mdb.storeUpdate(docName, updateWithAuthor); + await mdb.storeUpdate(docName, newUpdates); Y.applyUpdate(ydoc, Y.encodeStateAsUpdate(persistedYdoc)); ydoc.on('update', async (update) => { await mdb.storeUpdate(docName, update);