diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/2024-2-SCS4031-4tune_2.iml b/.idea/2024-2-SCS4031-4tune_2.iml new file mode 100644 index 0000000..d6ebd48 --- /dev/null +++ b/.idea/2024-2-SCS4031-4tune_2.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..033ceb7 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..0e5fb15 --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,16 @@ + + + + + + \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml new file mode 100644 index 0000000..fdc392f --- /dev/null +++ b/.idea/jarRepositories.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..e307c8b --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..1972af5 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules/Eyesee.main.iml b/.idea/modules/Eyesee.main.iml new file mode 100644 index 0000000..9a59fee --- /dev/null +++ b/.idea/modules/Eyesee.main.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/sonarlint/issuestore/2/0/20044058591931fd0315592ef1ff8f49b30c56a8 b/.idea/sonarlint/issuestore/2/0/20044058591931fd0315592ef1ff8f49b30c56a8 new file mode 100644 index 0000000..e69de29 diff --git a/.idea/sonarlint/issuestore/5/6/56dd6cb47a598105230d8373b803c1298611167e b/.idea/sonarlint/issuestore/5/6/56dd6cb47a598105230d8373b803c1298611167e new file mode 100644 index 0000000..bb94f84 --- /dev/null +++ b/.idea/sonarlint/issuestore/5/6/56dd6cb47a598105230d8373b803c1298611167e @@ -0,0 +1,3 @@ + +R +java:S1068"5Remove this unused "sessionRepository" private field.(ñ”ó8»ñü¡±2 \ No newline at end of file diff --git a/.idea/sonarlint/issuestore/6/b/6b7c031c5e32db2c4ce5b88cff90cfee6c383e7c b/.idea/sonarlint/issuestore/6/b/6b7c031c5e32db2c4ce5b88cff90cfee6c383e7c new file mode 100644 index 0000000..e69de29 diff --git a/.idea/sonarlint/issuestore/6/c/6c8ab44c3fafe8176dc7da142dfd25fba5934395 b/.idea/sonarlint/issuestore/6/c/6c8ab44c3fafe8176dc7da142dfd25fba5934395 new file mode 100644 index 0000000..e69de29 diff --git a/.idea/sonarlint/issuestore/7/a/7a4d608efaccfd4f4468751068d2deb4bb30812a b/.idea/sonarlint/issuestore/7/a/7a4d608efaccfd4f4468751068d2deb4bb30812a new file mode 100644 index 0000000..e69de29 diff --git a/.idea/sonarlint/issuestore/9/4/94dd8408f801d98d978be361720708fbbc21c4ea b/.idea/sonarlint/issuestore/9/4/94dd8408f801d98d978be361720708fbbc21c4ea new file mode 100644 index 0000000..e69de29 diff --git a/.idea/sonarlint/issuestore/a/5/a53dedd8a6394e7a1ea8b78f3a6a5ffa5eb5b725 b/.idea/sonarlint/issuestore/a/5/a53dedd8a6394e7a1ea8b78f3a6a5ffa5eb5b725 new file mode 100644 index 0000000..e69de29 diff --git a/.idea/sonarlint/issuestore/a/e/ae75e7400ba8be4928ee309dfea9b853dfff89c1 b/.idea/sonarlint/issuestore/a/e/ae75e7400ba8be4928ee309dfea9b853dfff89c1 new file mode 100644 index 0000000..e69de29 diff --git a/.idea/sonarlint/issuestore/c/3/c3cd8952bb3a7af153b678349a64d1a3e6275432 b/.idea/sonarlint/issuestore/c/3/c3cd8952bb3a7af153b678349a64d1a3e6275432 new file mode 100644 index 0000000..a1917c6 --- /dev/null +++ b/.idea/sonarlint/issuestore/c/3/c3cd8952bb3a7af153b678349a64d1a3e6275432 @@ -0,0 +1,6 @@ + +_ +java:S6813"BRemove this field injection and use constructor injection instead.(êæÄÞ8ûÕ…¢±2 +X java:S125" + + + + + \ No newline at end of file diff --git a/src/backend/Eyesee/build.gradle b/src/backend/Eyesee/build.gradle index 0ce4d86..e846d63 100644 --- a/src/backend/Eyesee/build.gradle +++ b/src/backend/Eyesee/build.gradle @@ -29,6 +29,10 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-webflux' implementation 'org.springframework.boot:spring-boot-starter-websocket' implementation 'org.springframework.boot:spring-boot-starter-security' + + // ëžœë¤ ì½”ë“œ ìƒì„± + implementation 'org.apache.commons:commons-lang3:3.12.0' + compileOnly 'org.projectlombok:lombok' runtimeOnly 'com.mysql:mysql-connector-j' annotationProcessor 'org.projectlombok:lombok' diff --git a/src/backend/Eyesee/src/main/java/com/fortune/eyesee/common/response/BaseResponseCode.java b/src/backend/Eyesee/src/main/java/com/fortune/eyesee/common/response/BaseResponseCode.java index 214ede5..6c3a259 100644 --- a/src/backend/Eyesee/src/main/java/com/fortune/eyesee/common/response/BaseResponseCode.java +++ b/src/backend/Eyesee/src/main/java/com/fortune/eyesee/common/response/BaseResponseCode.java @@ -42,13 +42,10 @@ public enum BaseResponseCode { // Exam Errors NOT_FOUND_EXAM("E0001", HttpStatus.NOT_FOUND, "ì‹œí—˜ì„ ì°¾ì„ ìˆ˜ 없습니다."), NOT_FOUND_EXAM_CODE("E0002", HttpStatus.NOT_FOUND, "시험 코드를 ì°¾ì„ ìˆ˜ 없습니다."), - - // Exam Errors - EXAM_NOT_FOUND("E0001", HttpStatus.NOT_FOUND, "ì‹œí—˜ì„ ì°¾ì„ ìˆ˜ 없습니다."), - SESSION_NOT_FOUND("E0002", HttpStatus.NOT_FOUND, "ì„¸ì…˜ì„ ì°¾ì„ ìˆ˜ 없습니다."), - EXAM_ALREADY_EXISTS("E0003", HttpStatus.CONFLICT, "ì´ë¯¸ 존재하는 시험입니다."), - INVALID_EXAM_STATUS("E0004", HttpStatus.BAD_REQUEST, "유효하지 ì•Šì€ ì‹œí—˜ ìƒíƒœìž…니다."), - EXAM_ACCESS_DENIED("E0005", HttpStatus.FORBIDDEN, "ì‹œí—˜ì— ì ‘ê·¼í•  ê¶Œí•œì´ ì—†ìŠµë‹ˆë‹¤."), + NOT_FOUND_SESSION("E0003", HttpStatus.NOT_FOUND, "ì„¸ì…˜ì„ ì°¾ì„ ìˆ˜ 없습니다."), + EXAM_ALREADY_EXISTS("E0004", HttpStatus.CONFLICT, "ì´ë¯¸ 존재하는 시험입니다."), + INVALID_EXAM_STATUS("E0005", HttpStatus.BAD_REQUEST, "유효하지 ì•Šì€ ì‹œí—˜ ìƒíƒœìž…니다."), + EXAM_ACCESS_DENIED("E0006", HttpStatus.FORBIDDEN, "ì‹œí—˜ì— ì ‘ê·¼í•  ê¶Œí•œì´ ì—†ìŠµë‹ˆë‹¤."), // 기타 추가 오류 코드 ... diff --git a/src/backend/Eyesee/src/main/java/com/fortune/eyesee/controller/ExamController.java b/src/backend/Eyesee/src/main/java/com/fortune/eyesee/controller/ExamController.java index efb1291..4aaefeb 100644 --- a/src/backend/Eyesee/src/main/java/com/fortune/eyesee/controller/ExamController.java +++ b/src/backend/Eyesee/src/main/java/com/fortune/eyesee/controller/ExamController.java @@ -3,10 +3,7 @@ import com.fortune.eyesee.common.exception.BaseException; import com.fortune.eyesee.common.response.BaseResponse; import com.fortune.eyesee.common.response.BaseResponseCode; -import com.fortune.eyesee.dto.ExamCodeRequestDTO; -import com.fortune.eyesee.dto.ExamResponseDTO; -import com.fortune.eyesee.dto.UserDetailResponseDTO; -import com.fortune.eyesee.dto.UserListResponseDTO; +import com.fortune.eyesee.dto.*; import com.fortune.eyesee.enums.ExamStatus; import com.fortune.eyesee.service.ExamService; import org.springframework.beans.factory.annotation.Autowired; @@ -15,7 +12,10 @@ import org.springframework.web.bind.annotation.*; import jakarta.servlet.http.HttpSession; + +import java.util.HashMap; import java.util.List; +import java.util.Map; @RestController @RequestMapping("/api/exams") @@ -58,6 +58,35 @@ public class ExamController { // return ResponseEntity.ok(new BaseResponse<>(examList)); // } + // 시험 ë“±ë¡ +// @PostMapping +// public ResponseEntity> registerExam(@RequestBody ExamRequestDTO examRequestDTO, HttpSession session) { +// Integer adminId = (Integer) session.getAttribute("adminId"); +// if (adminId == null) { +// // 로그ì¸ë˜ì§€ ì•Šì€ ê²½ìš° 예외처리 +// throw new BaseException(BaseResponseCode.UNAUTHORIZED); +// } +// +// // 시험 ìƒì„± ë° ëžœë¤ ì½”ë“œ ìƒì„± +// ExamResponseDTO response = examService.registerExam(adminId, examRequestDTO); +// return ResponseEntity.ok(new BaseResponse<>(response, "시험 등ë¡ì— 성공했습니다.")); +// } + @PostMapping + public ResponseEntity>> registerExam(@RequestBody ExamRequestDTO examRequestDTO, HttpSession session) { + Integer adminId = (Integer) session.getAttribute("adminId"); + if (adminId == null) { + throw new BaseException(BaseResponseCode.UNAUTHORIZED); + } + + ExamResponseDTO response = examService.registerExam(adminId, examRequestDTO); + + // examRandomCode만 í¬í•¨ëœ ì‘답 ìƒì„± + Map responseData = new HashMap<>(); + responseData.put("examRandomCode", response.getExamRandomCode()); + + return ResponseEntity.ok(new BaseResponse<>(responseData, "시험 등ë¡ì— 성공했습니다.")); + } + // "before" ìƒíƒœì˜ Exam 리스트 조회 @GetMapping("/before") public ResponseEntity>> getBeforeExams() { @@ -127,4 +156,4 @@ public ResponseEntity> getUserDetailByExamId UserDetailResponseDTO response = examService.getUserDetailByExamIdAndUserId(examId, userId); return ResponseEntity.ok(new BaseResponse<>(response, "í•™ìƒ ìƒì„¸ ì •ë³´ 조회 성공")); } -} +} \ No newline at end of file diff --git a/src/backend/Eyesee/src/main/java/com/fortune/eyesee/controller/SessionController.java b/src/backend/Eyesee/src/main/java/com/fortune/eyesee/controller/SessionController.java new file mode 100644 index 0000000..2c7adaf --- /dev/null +++ b/src/backend/Eyesee/src/main/java/com/fortune/eyesee/controller/SessionController.java @@ -0,0 +1,35 @@ +package com.fortune.eyesee.controller; + +import com.fortune.eyesee.common.response.BaseResponse; +import com.fortune.eyesee.dto.ExamCodeRequestDTO; +import com.fortune.eyesee.dto.UserInfoRequestDTO; +import com.fortune.eyesee.service.SessionService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/api/sessions") +public class SessionController { + + private final SessionService sessionService; + + @Autowired + public SessionController(SessionService sessionService) { + this.sessionService = sessionService; + } + + // 시험 세션 입장 + @PostMapping("/join") + public ResponseEntity> joinExam(@RequestBody ExamCodeRequestDTO examCodeRequestDTO) { + sessionService.joinExamSession(examCodeRequestDTO.getExamCode()); + return ResponseEntity.ok(new BaseResponse<>("시험 세션 ìž…ìž¥ì— ì„±ê³µí–ˆìŠµë‹ˆë‹¤.")); + } + + // ì‚¬ìš©ìž ì •ë³´ ìž…ë ¥ + @PostMapping("/student") + public ResponseEntity> addUserInfo(@RequestBody UserInfoRequestDTO userInfoRequestDTO) { + sessionService.addUserInfo(userInfoRequestDTO); + return ResponseEntity.ok(new BaseResponse<>("ì‚¬ìš©ìž ì •ë³´ ìž…ë ¥ì— ì„±ê³µí–ˆìŠµë‹ˆë‹¤.")); + } +} \ No newline at end of file diff --git a/src/backend/Eyesee/src/main/java/com/fortune/eyesee/dto/ExamRequestDTO.java b/src/backend/Eyesee/src/main/java/com/fortune/eyesee/dto/ExamRequestDTO.java new file mode 100644 index 0000000..8888337 --- /dev/null +++ b/src/backend/Eyesee/src/main/java/com/fortune/eyesee/dto/ExamRequestDTO.java @@ -0,0 +1,20 @@ +package com.fortune.eyesee.dto; + +import lombok.Data; + +import java.time.LocalDate; +import java.time.LocalTime; + +@Data +public class ExamRequestDTO { + private String examName; + private String examSemester; + private Integer examStudentNumber; + private String examLocation; + private LocalDate examDate; + private LocalTime examStartTime; + private Integer examDuration; + private Integer examQuestionNumber; + private Integer examTotalScore; + private String examNotice; +} \ No newline at end of file diff --git a/src/backend/Eyesee/src/main/java/com/fortune/eyesee/dto/UserInfoRequestDTO.java b/src/backend/Eyesee/src/main/java/com/fortune/eyesee/dto/UserInfoRequestDTO.java new file mode 100644 index 0000000..2536b2c --- /dev/null +++ b/src/backend/Eyesee/src/main/java/com/fortune/eyesee/dto/UserInfoRequestDTO.java @@ -0,0 +1,12 @@ + +package com.fortune.eyesee.dto; + +import lombok.Data; + +@Data +public class UserInfoRequestDTO { + private String name; // ì‚¬ìš©ìž ì´ë¦„ + private String department; // 학과 + private Integer userNum; // 학번 + private Integer seatNum; // ì¢Œì„ ë²ˆí˜¸ +} diff --git a/src/backend/Eyesee/src/main/java/com/fortune/eyesee/service/ExamService.java b/src/backend/Eyesee/src/main/java/com/fortune/eyesee/service/ExamService.java index cd80265..012a7a6 100644 --- a/src/backend/Eyesee/src/main/java/com/fortune/eyesee/service/ExamService.java +++ b/src/backend/Eyesee/src/main/java/com/fortune/eyesee/service/ExamService.java @@ -2,12 +2,14 @@ import com.fortune.eyesee.common.exception.BaseException; import com.fortune.eyesee.common.response.BaseResponseCode; +import com.fortune.eyesee.dto.ExamRequestDTO; import com.fortune.eyesee.dto.ExamResponseDTO; import com.fortune.eyesee.dto.UserDetailResponseDTO; import com.fortune.eyesee.dto.UserListResponseDTO; import com.fortune.eyesee.entity.*; import com.fortune.eyesee.enums.ExamStatus; import com.fortune.eyesee.repository.*; +import org.apache.commons.lang3.RandomStringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -26,6 +28,7 @@ public class ExamService { private final VideoRecordingRepository videoRecordingRepository; private final DetectedCheatingRepository detectedCheatingRepository; private final CheatingTypeRepository cheatingTypeRepository; + private final AdminRepository adminRepository; @Autowired public ExamService(ExamRepository examRepository, @@ -34,7 +37,7 @@ public ExamService(ExamRepository examRepository, CheatingStatisticsRepository cheatingStatisticsRepository, VideoRecordingRepository videoRecordingRepository, DetectedCheatingRepository detectedCheatingRepository, - CheatingTypeRepository cheatingTypeRepository) { + CheatingTypeRepository cheatingTypeRepository, AdminRepository adminRepository) { this.examRepository = examRepository; this.userRepository = userRepository; this.sessionRepository = sessionRepository; @@ -42,6 +45,55 @@ public ExamService(ExamRepository examRepository, this.videoRecordingRepository = videoRecordingRepository; this.detectedCheatingRepository = detectedCheatingRepository; this.cheatingTypeRepository = cheatingTypeRepository; + this.adminRepository = adminRepository; + } + // 시험 ë“±ë¡ ë©”ì„œë“œ + public ExamResponseDTO registerExam(Integer adminId, ExamRequestDTO examRequestDTO) { + Admin admin = adminRepository.findById(adminId) + .orElseThrow(() -> new BaseException(BaseResponseCode.UNAUTHORIZED)); + + // 10ìžë¦¬ ëžœë¤ ì˜ìˆ«ìž 코드 ìƒì„± + String examRandomCode = RandomStringUtils.randomAlphanumeric(10); + + // Exam ìƒì„± + Exam exam = new Exam(); + exam.setAdmin(admin); + exam.setExamName(examRequestDTO.getExamName()); + exam.setExamSemester(examRequestDTO.getExamSemester()); + exam.setExamStudentNumber(examRequestDTO.getExamStudentNumber()); + exam.setExamLocation(examRequestDTO.getExamLocation()); + exam.setExamDate(examRequestDTO.getExamDate()); + exam.setExamStartTime(examRequestDTO.getExamStartTime()); + exam.setExamDuration(examRequestDTO.getExamDuration()); + exam.setExamQuestionNumber(examRequestDTO.getExamQuestionNumber()); + exam.setExamTotalScore(examRequestDTO.getExamTotalScore()); + exam.setExamNotice(examRequestDTO.getExamNotice()); + exam.setExamStatus(ExamStatus.BEFORE); + exam.setExamRandomCode(examRandomCode); + + // Exam 저장 후 ID ìƒì„± + examRepository.save(exam); + + // ExamId를 사용해 Session ìƒì„± ë° ì„¤ì • + Session session = new Session(); + session.setSessionId(exam.getExamId()); // ExamId와 ë™ì¼í•˜ê²Œ 설정 + session.setExam(exam); + sessionRepository.save(session); + + return new ExamResponseDTO( + exam.getExamId(), + exam.getExamName(), + exam.getExamSemester(), + exam.getExamStudentNumber(), + exam.getExamLocation(), + exam.getExamDate(), + exam.getExamStartTime(), + exam.getExamDuration(), + exam.getExamStatus(), + exam.getExamNotice(), + session.getSessionId(), + exam.getExamRandomCode() + ); } // 특정 시험 IDë¡œ 존재 여부 í™•ì¸ diff --git a/src/backend/Eyesee/src/main/java/com/fortune/eyesee/service/SessionService.java b/src/backend/Eyesee/src/main/java/com/fortune/eyesee/service/SessionService.java new file mode 100644 index 0000000..ae40e62 --- /dev/null +++ b/src/backend/Eyesee/src/main/java/com/fortune/eyesee/service/SessionService.java @@ -0,0 +1,50 @@ +package com.fortune.eyesee.service; + +import com.fortune.eyesee.common.exception.BaseException; +import com.fortune.eyesee.common.response.BaseResponseCode; +import com.fortune.eyesee.dto.ExamCodeRequestDTO; +import com.fortune.eyesee.dto.UserInfoRequestDTO; +import com.fortune.eyesee.entity.Exam; +import com.fortune.eyesee.entity.User; +import com.fortune.eyesee.repository.ExamRepository; +import com.fortune.eyesee.repository.UserRepository; +import com.fortune.eyesee.repository.SessionRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class SessionService { + + private final ExamRepository examRepository; + private final UserRepository userRepository; + private final SessionRepository sessionRepository; + + @Autowired + public SessionService(ExamRepository examRepository, UserRepository userRepository, SessionRepository sessionRepository) { + this.examRepository = examRepository; + this.userRepository = userRepository; + this.sessionRepository = sessionRepository; + } + + public boolean joinExamSession(String examCode) { + Exam exam = examRepository.findByExamRandomCode(examCode); + if (exam == null) { + throw new BaseException(BaseResponseCode.NOT_FOUND_EXAM_CODE); + } + if (exam.getSession() == null) { + throw new BaseException(BaseResponseCode.NOT_FOUND_SESSION); + } + return true; + } + + // ì‚¬ìš©ìž ì •ë³´ 저장 + public void addUserInfo(UserInfoRequestDTO userInfoRequestDTO) { + User user = new User(); + user.setUserName(userInfoRequestDTO.getName()); + user.setDepartment(userInfoRequestDTO.getDepartment()); + user.setUserNum(userInfoRequestDTO.getUserNum()); + user.setSeatNum(userInfoRequestDTO.getSeatNum()); + + userRepository.save(user); + } +} \ No newline at end of file