diff --git a/src/main/java/turing/turing/domain/studyRoom/StudyRoomController.java b/src/main/java/turing/turing/domain/studyRoom/StudyRoomController.java index 637972a..6734fcc 100644 --- a/src/main/java/turing/turing/domain/studyRoom/StudyRoomController.java +++ b/src/main/java/turing/turing/domain/studyRoom/StudyRoomController.java @@ -2,6 +2,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; @@ -27,7 +28,7 @@ public class StudyRoomController { @Operation(summary = "[선생님] 과외 정보 등록") @PostMapping - public ResponseEntity createStudyRoom(@AuthenticationPrincipal CustomUserDetails user, @RequestBody StudyRoomCreateReqDto studyRoomCreateReqDto){ + public ResponseEntity createStudyRoom(@AuthenticationPrincipal CustomUserDetails user, @Valid @RequestBody StudyRoomCreateReqDto studyRoomCreateReqDto){ Long teacherId = user.getMemberId(); Long studyRoomId = studyRoomService.createStudyRoom(teacherId, studyRoomCreateReqDto); return ResponseEntity.ok(studyRoomId); @@ -35,7 +36,7 @@ public ResponseEntity createStudyRoom(@AuthenticationPrincipal CustomUserD @Operation(summary = "[선생님] 과외 정보 수정") @PutMapping("/{studyRoomId}") - public ResponseEntity updateStudyRoom(@PathVariable Long studyRoomId, @RequestBody StudyRoomUpdateReqDto studyRoomUpdateReqDto){ + public ResponseEntity updateStudyRoom(@PathVariable Long studyRoomId, @Valid @RequestBody StudyRoomUpdateReqDto studyRoomUpdateReqDto){ studyRoomService.updateStudyRoom(studyRoomId, studyRoomUpdateReqDto); return ResponseEntity.ok().build(); } @@ -89,7 +90,7 @@ public ResponseEntity> getStudyRooms(@AuthenticationPrinci @GetMapping("/{studyRoomId}") public ResponseEntity getDetailedStudyRooms(@AuthenticationPrincipal CustomUserDetails user, @PathVariable Long studyRoomId){ Role role = user.getRole(); - DetailedStudyRoomResDto detailedStudyRoomResDto = studyRoomService.getDetailedStudyRooms(studyRoomId, role); + DetailedStudyRoomResDto detailedStudyRoomResDto = studyRoomService.getDetailedStudyRoom(studyRoomId, role); return ResponseEntity.ok(detailedStudyRoomResDto); } diff --git a/src/main/java/turing/turing/domain/studyRoom/StudyRoomService.java b/src/main/java/turing/turing/domain/studyRoom/StudyRoomService.java index 9775fd6..e5d0c2f 100644 --- a/src/main/java/turing/turing/domain/studyRoom/StudyRoomService.java +++ b/src/main/java/turing/turing/domain/studyRoom/StudyRoomService.java @@ -1,238 +1,37 @@ package turing.turing.domain.studyRoom; -import java.util.ArrayList; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import turing.turing.domain.code.ConnectionCode; -import turing.turing.domain.code.ConnectionCodeRepository; import turing.turing.domain.member.Role; -import turing.turing.domain.schedule.Schedule; -import turing.turing.domain.schedule.ScheduleRepository; -import turing.turing.domain.schedule.ScheduleService; -import turing.turing.domain.schedule.dto.CreateScheduleRequest; import turing.turing.domain.studyRoom.dto.BaseTemplateDto; -import turing.turing.domain.student.Student; -import turing.turing.domain.student.StudentRepository; -import turing.turing.domain.studyRoom.dto.response.DetailedStudyRoomResDto; import turing.turing.domain.studyRoom.dto.request.StudyRoomCreateReqDto; -import turing.turing.domain.studyRoom.dto.response.StudyRoomResDto; import turing.turing.domain.studyRoom.dto.request.StudyRoomUpdateReqDto; +import turing.turing.domain.studyRoom.dto.response.DetailedStudyRoomResDto; +import turing.turing.domain.studyRoom.dto.response.StudyRoomResDto; import turing.turing.domain.studyRoom.dto.response.SubjectAndTeacherResDto; -import turing.turing.domain.studyTime.StudyTime; -import turing.turing.domain.studyTime.StudyTimeRepository; -import turing.turing.domain.studyTime.dto.StudyTimeResDto; -import turing.turing.domain.teacher.Teacher; -import turing.turing.domain.teacher.TeacherRepository; -import turing.turing.global.exception.RestApiException; -import turing.turing.global.exception.errorCode.StudyRoomErrorCode; -import turing.turing.global.exception.errorCode.UserErrorCode; import java.util.List; -import java.util.concurrent.ThreadLocalRandom; - -@Service -@RequiredArgsConstructor -@Transactional(readOnly = true) -public class StudyRoomService { - - private final StudyRoomRepository studyRoomRepository; - private final TeacherRepository teacherRepository; - private final StudentRepository studentRepository; - private final StudyTimeRepository studyTimeRepository; - private final ConnectionCodeRepository connectionCodeRepository; - private final ScheduleRepository scheduleRepository; - private final ScheduleService scheduleService; - - @Transactional - public Long createStudyRoom(Long teacherId, StudyRoomCreateReqDto studyRoomCreateReqDto) { - - Teacher teacher = teacherRepository.findById(teacherId) - .orElseThrow(() -> new RestApiException(UserErrorCode.TEACHER_NOT_FOUND)); - - Student student = studyRoomCreateReqDto.toStudent(); - studentRepository.save(student); - - StudyRoom studyRoom = studyRoomCreateReqDto.toStudyRoom(teacher, student); - studyRoomRepository.save(studyRoom); - - List studyTimes = studyRoomCreateReqDto.studyTimes().stream().map(studyTimeReqDto -> studyTimeReqDto.toEntity(studyRoom)).toList(); - studyTimeRepository.saveAll(studyTimes); - - - // 기준회차 생성 - CreateScheduleRequest createScheduleRequest = new CreateScheduleRequest( - studyRoom.getId(), - studyRoomCreateReqDto.studentLastName() + studyRoomCreateReqDto.studentFirstName(), - studyRoomCreateReqDto.subject(), - studyRoomCreateReqDto.studyTimes(), - studyRoomCreateReqDto.baseSession(), - studyRoomCreateReqDto.startDate() - ); - scheduleService.createSchedules(createScheduleRequest); - - return studyRoom.getId(); - } - - @Transactional - public void updateStudyRoom(Long studyRoomId, StudyRoomUpdateReqDto studyRoomUpdateReqDto) { - - StudyRoom studyRoom = studyRoomRepository.findById(studyRoomId) - .orElseThrow(() -> new RestApiException(StudyRoomErrorCode.STUDY_ROOM_NOT_FOUND)); - - studyRoom.updateStudyRoom(studyRoomUpdateReqDto.subject(), studyRoomUpdateReqDto.baseSession(), studyRoomUpdateReqDto.wage()); - - // StudyTime의 경우, 요일이 추가/삭제될 수도 있기 때문에 변경 감지가 아닌 기존 StudyTime 삭제 후 다시 생성하도록 함 - studyTimeRepository.deleteByStudyRoomId(studyRoomId); // 벌크 연산을 통해 삭제 - List studyTimes = studyRoomUpdateReqDto.studyTimes().stream().map(studyTimeReqDto -> studyTimeReqDto.toEntity(studyRoom)).toList(); - studyTimeRepository.saveAll(studyTimes); - } - - @Transactional - public void deleteStudyRoom(Long studyRoomId){ - - StudyRoom studyRoom = studyRoomRepository.findById(studyRoomId) - .orElseThrow(() -> new RestApiException(StudyRoomErrorCode.STUDY_ROOM_NOT_FOUND)); - - studyRoomRepository.delete(studyRoom); - } - - @Transactional - public Integer getConnectionCode(Long studyRoomId){ - - StudyRoom studyRoom = studyRoomRepository.findById(studyRoomId) - .orElseThrow(() -> new RestApiException(StudyRoomErrorCode.STUDY_ROOM_NOT_FOUND)); - - // 이미 연결된 경우에는 코드를 생성하지 않음 - if(studyRoom.getLinkStatus()) - throw new RestApiException(StudyRoomErrorCode.ALREADY_CONNECTED_STUDY_ROOM); - - // 이미 연결 코드가 존재한다면 그대로 리턴, 존재하지 않는다면 중복되지 않는 연결 코드 생성하여 리턴 - return connectionCodeRepository.findByStudyRoom(studyRoom) - .map(ConnectionCode::getCode) - .orElseGet(() -> { - ConnectionCode connectionCode = new ConnectionCode(generateCode(), studyRoom); - connectionCodeRepository.save(connectionCode); - return connectionCode.getCode(); - }); - } - - public SubjectAndTeacherResDto getTeacherByCode(Integer code){ - - ConnectionCode connectionCode = connectionCodeRepository.findWithStudyRoomAndTeacherByCode(code) - .orElseThrow(() -> new RestApiException(StudyRoomErrorCode.CONNECTION_CODE_NOT_FOUND)); - - String subject = connectionCode.getStudyRoom().getSubject(); - String firstName = connectionCode.getStudyRoom().getTeacher().getFirstName(); - String lastName = connectionCode.getStudyRoom().getTeacher().getLastName(); - - return new SubjectAndTeacherResDto(subject, firstName, lastName); - } - - @Transactional - public void connectTeacherStudent(Long studentId, Integer code){ - - Student student = studentRepository.findById(studentId) - .orElseThrow(() -> new RestApiException(UserErrorCode.STUDENT_NOT_FOUND)); - - ConnectionCode connectionCode = connectionCodeRepository.findWithStudyRoomAndStudentByCode(code) - .orElseThrow(() -> new RestApiException(StudyRoomErrorCode.CONNECTION_CODE_NOT_FOUND)); - - // 기존 학생 가져오기 - Student nonSignUpStudent = connectionCode.getStudyRoom().getStudent(); - - // 학생 정보를 업데이트하고, 실제로 가입한 학생과 연결 (참조를 변경) - student.updateSchoolAndYear(nonSignUpStudent.getSchool(), nonSignUpStudent.getYear()); - connectionCode.getStudyRoom().connectStudent(student); - - // 기존 학생은 삭제 - studentRepository.delete(nonSignUpStudent); - - // 연결 후, 연결 코드는 삭제됨 - connectionCodeRepository.delete(connectionCode); - } - - @Transactional - public void disconnectTeacherStudent(Long studyRoomId){ - - StudyRoom studyRoom = studyRoomRepository.findWithStudentById(studyRoomId) - .orElseThrow(() -> new RestApiException(StudyRoomErrorCode.STUDY_ROOM_NOT_FOUND)); - - // 기존 학생의 정보를 통해 nonSignedUpStudent 생성 - Student nonSignUpStudent = new Student( - studyRoom.getStudent().getFirstName(), - studyRoom.getStudent().getLastName(), - studyRoom.getStudent().getSchool(), - studyRoom.getStudent().getYear(), - studyRoom.getStudent().getPhone(), - studyRoom.getStudent().getParentPhone() - ); - studentRepository.save(nonSignUpStudent); - studyRoom.disconnectStudent(nonSignUpStudent); - } - - public List getStudyRooms(Role role, Long memberId){ - - List studyRoomList; - if(role == Role.TEACHER){ - Teacher teacher = teacherRepository.findById(memberId) - .orElseThrow(() -> new RestApiException(UserErrorCode.TEACHER_NOT_FOUND)); - - studyRoomList = studyRoomRepository.findAllWithStudentByTeacher(teacher); - - } else { - Student student = studentRepository.findById(memberId) - .orElseThrow(() -> new RestApiException(UserErrorCode.STUDENT_NOT_FOUND)); - studyRoomList = studyRoomRepository.findAllWithTeacherByStudent(student); - } +public interface StudyRoomService { - List studyRoomResDtoList = studyRoomList.stream().map(studyRoom -> StudyRoomResDto.of(studyRoom, role)).toList(); - return studyRoomResDtoList; - } + Long createStudyRoom(Long teacherId, StudyRoomCreateReqDto studyRoomCreateReqDto); - public DetailedStudyRoomResDto getDetailedStudyRooms(Long studyRoomId, Role role){ + void updateStudyRoom(Long studyRoomId, StudyRoomUpdateReqDto studyRoomUpdateReqDto); - StudyRoom studyRoom; - List scheduleList; + void deleteStudyRoom(Long studyRoomId); - if(role == Role.TEACHER) { - studyRoom = studyRoomRepository.findWithAllStudyTimeAndStudentById(studyRoomId) - .orElseThrow(() -> new RestApiException(StudyRoomErrorCode.STUDY_ROOM_NOT_FOUND)); - } else { - studyRoom = studyRoomRepository.findWithAllStudyTimeAndTeacherById(studyRoomId) - .orElseThrow(() -> new RestApiException(StudyRoomErrorCode.STUDY_ROOM_NOT_FOUND)); - } + Integer getConnectionCode(Long studyRoomId); - scheduleList = scheduleRepository.findAllByStudyRoomOrderByDate(studyRoom); - return DetailedStudyRoomResDto.of(studyRoom, scheduleList, role); - } + SubjectAndTeacherResDto getTeacherByCode(Integer code); - // 중복되지 않는 6자리 연결 코드를 생성함 - public Integer generateCode(){ - while (true){ - Integer generatedCode = ThreadLocalRandom.current().nextInt(100000, 1000000); // 6자리 코드 생성 + void connectTeacherStudent(Long studentId, Integer code); - if(!connectionCodeRepository.existsByCode(generatedCode)) // 중복되지 않는 코드인지 검증 - return generatedCode; - } - } + void disconnectTeacherStudent(Long studyRoomId); - public BaseTemplateDto getBaseTemplate(Long studyRoomId) { + List getStudyRooms(Role role, Long memberId); - List studyTimeList = studyTimeRepository.findByStudyRoomId(studyRoomId); - if (studyTimeList.isEmpty()) { - throw new RestApiException(StudyRoomErrorCode.STUDY_TIME_NOT_FOUND); - } + DetailedStudyRoomResDto getDetailedStudyRoom(Long studyRoomId, Role role); - List studyTimeDtoList = new ArrayList<>(); - for (StudyTime st : studyTimeList) { - studyTimeDtoList.add(StudyTimeResDto.of(st)); - } + Integer generateCode(); - StudyRoom studyRoom = studyTimeList.get(0).getStudyRoom(); + BaseTemplateDto getBaseTemplate(Long studyRoomId); - return new BaseTemplateDto(studyRoomId, studyTimeDtoList, studyRoom.getBaseSession(), - studyRoom.getWage()); - } -} +} \ No newline at end of file diff --git a/src/main/java/turing/turing/domain/studyRoom/StudyRoomServiceImpl.java b/src/main/java/turing/turing/domain/studyRoom/StudyRoomServiceImpl.java new file mode 100644 index 0000000..4608197 --- /dev/null +++ b/src/main/java/turing/turing/domain/studyRoom/StudyRoomServiceImpl.java @@ -0,0 +1,245 @@ +package turing.turing.domain.studyRoom; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import turing.turing.domain.code.ConnectionCode; +import turing.turing.domain.code.ConnectionCodeRepository; +import turing.turing.domain.member.Role; +import turing.turing.domain.schedule.Schedule; +import turing.turing.domain.schedule.ScheduleRepository; +import turing.turing.domain.schedule.ScheduleService; +import turing.turing.domain.schedule.dto.CreateScheduleRequest; +import turing.turing.domain.student.Student; +import turing.turing.domain.student.StudentRepository; +import turing.turing.domain.studyRoom.dto.BaseTemplateDto; +import turing.turing.domain.studyRoom.dto.request.StudyRoomCreateReqDto; +import turing.turing.domain.studyRoom.dto.request.StudyRoomUpdateReqDto; +import turing.turing.domain.studyRoom.dto.response.DetailedStudyRoomResDto; +import turing.turing.domain.studyRoom.dto.response.StudyRoomResDto; +import turing.turing.domain.studyRoom.dto.response.SubjectAndTeacherResDto; +import turing.turing.domain.studyTime.StudyTime; +import turing.turing.domain.studyTime.StudyTimeRepository; +import turing.turing.domain.studyTime.dto.StudyTimeResDto; +import turing.turing.domain.teacher.Teacher; +import turing.turing.domain.teacher.TeacherRepository; +import turing.turing.global.exception.RestApiException; +import turing.turing.global.exception.errorCode.StudyRoomErrorCode; +import turing.turing.global.exception.errorCode.UserErrorCode; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ThreadLocalRandom; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class StudyRoomServiceImpl implements StudyRoomService{ + + private final StudyRoomRepository studyRoomRepository; + private final TeacherRepository teacherRepository; + private final StudentRepository studentRepository; + private final StudyTimeRepository studyTimeRepository; + private final ConnectionCodeRepository connectionCodeRepository; + private final ScheduleRepository scheduleRepository; + private final ScheduleService scheduleService; + + @Transactional + public Long createStudyRoom(Long teacherId, StudyRoomCreateReqDto studyRoomCreateReqDto) { + + Teacher teacher = teacherRepository.findById(teacherId) + .orElseThrow(() -> new RestApiException(UserErrorCode.TEACHER_NOT_FOUND)); + + Student student = studyRoomCreateReqDto.toStudent(); + studentRepository.save(student); + + StudyRoom studyRoom = studyRoomCreateReqDto.toStudyRoom(teacher, student); + studyRoomRepository.save(studyRoom); + + List studyTimes = studyRoomCreateReqDto.studyTimes().stream().map(studyTimeReqDto -> studyTimeReqDto.toEntity(studyRoom)).toList(); + studyRoom.getStudyTimes().addAll(studyTimes); // 양방향 연관관계이므로, 양쪽 방향에 모두 값을 입력해주자 (테스트에서 문제 발생하였기 때문에 추가함) + studyTimeRepository.saveAll(studyTimes); + + // 기준회차 생성 + CreateScheduleRequest createScheduleRequest = new CreateScheduleRequest( + studyRoom.getId(), + studyRoomCreateReqDto.studentLastName() + studyRoomCreateReqDto.studentFirstName(), + studyRoomCreateReqDto.subject(), + studyRoomCreateReqDto.studyTimes(), + studyRoomCreateReqDto.baseSession(), + studyRoomCreateReqDto.startDate() + ); + scheduleService.createSchedules(createScheduleRequest); + + return studyRoom.getId(); + } + + @Transactional + public void updateStudyRoom(Long studyRoomId, StudyRoomUpdateReqDto studyRoomUpdateReqDto) { + + StudyRoom studyRoom = studyRoomRepository.findById(studyRoomId) + .orElseThrow(() -> new RestApiException(StudyRoomErrorCode.STUDY_ROOM_NOT_FOUND)); + + studyRoom.updateStudyRoom(studyRoomUpdateReqDto.subject(), studyRoomUpdateReqDto.baseSession(), studyRoomUpdateReqDto.wage()); + + // StudyTime의 경우, 요일이 추가/삭제될 수도 있기 때문에 변경 감지가 아닌 기존 StudyTime 삭제 후 다시 생성하도록 함 + studyTimeRepository.deleteByStudyRoomId(studyRoomId); // 벌크 연산을 통해 삭제 + studyRoom.getStudyTimes().clear(); + List studyTimes = studyRoomUpdateReqDto.studyTimes().stream().map(studyTimeReqDto -> studyTimeReqDto.toEntity(studyRoom)).toList(); + studyRoom.getStudyTimes().addAll(studyTimes); // 양방향 연관관계이므로, 양쪽 방향에 모두 값을 입력해주자 + studyTimeRepository.saveAll(studyTimes); + } + + @Transactional + public void deleteStudyRoom(Long studyRoomId){ + + StudyRoom studyRoom = studyRoomRepository.findById(studyRoomId) + .orElseThrow(() -> new RestApiException(StudyRoomErrorCode.STUDY_ROOM_NOT_FOUND)); + + studyRoomRepository.delete(studyRoom); + } + + @Transactional + public Integer getConnectionCode(Long studyRoomId){ + + StudyRoom studyRoom = studyRoomRepository.findById(studyRoomId) + .orElseThrow(() -> new RestApiException(StudyRoomErrorCode.STUDY_ROOM_NOT_FOUND)); + + // 이미 연결된 경우에는 코드를 생성하지 않음 + if(studyRoom.getLinkStatus()) + throw new RestApiException(StudyRoomErrorCode.ALREADY_CONNECTED_STUDY_ROOM); + + // 이미 연결 코드가 존재한다면 그대로 리턴, 존재하지 않는다면 중복되지 않는 연결 코드 생성하여 리턴 + return connectionCodeRepository.findByStudyRoom(studyRoom) + .map(ConnectionCode::getCode) + .orElseGet(() -> { + ConnectionCode connectionCode = new ConnectionCode(generateCode(), studyRoom); + connectionCodeRepository.save(connectionCode); + return connectionCode.getCode(); + }); + } + + public SubjectAndTeacherResDto getTeacherByCode(Integer code){ + + ConnectionCode connectionCode = connectionCodeRepository.findWithStudyRoomAndTeacherByCode(code) + .orElseThrow(() -> new RestApiException(StudyRoomErrorCode.CONNECTION_CODE_NOT_FOUND)); + + String subject = connectionCode.getStudyRoom().getSubject(); + String firstName = connectionCode.getStudyRoom().getTeacher().getFirstName(); + String lastName = connectionCode.getStudyRoom().getTeacher().getLastName(); + + return new SubjectAndTeacherResDto(subject, firstName, lastName); + } + + @Transactional + public void connectTeacherStudent(Long studentId, Integer code){ + + Student student = studentRepository.findById(studentId) + .orElseThrow(() -> new RestApiException(UserErrorCode.STUDENT_NOT_FOUND)); + + ConnectionCode connectionCode = connectionCodeRepository.findWithStudyRoomAndStudentByCode(code) + .orElseThrow(() -> new RestApiException(StudyRoomErrorCode.CONNECTION_CODE_NOT_FOUND)); + + // 기존 학생 가져오기 + Student nonSignUpStudent = connectionCode.getStudyRoom().getStudent(); + + // 학생 정보를 업데이트하고, 실제로 가입한 학생과 연결 (참조를 변경) + student.updateSchoolAndYear(nonSignUpStudent.getSchool(), nonSignUpStudent.getYear()); + connectionCode.getStudyRoom().connectStudent(student); + + // 기존 학생은 삭제 + studentRepository.delete(nonSignUpStudent); + + // 연결 후, 연결 코드는 삭제됨 + connectionCodeRepository.delete(connectionCode); + } + + @Transactional + public void disconnectTeacherStudent(Long studyRoomId){ + + StudyRoom studyRoom = studyRoomRepository.findWithStudentById(studyRoomId) + .orElseThrow(() -> new RestApiException(StudyRoomErrorCode.STUDY_ROOM_NOT_FOUND)); + + // 기존 학생의 정보를 통해 nonSignedUpStudent 생성 + Student nonSignUpStudent = new Student( + studyRoom.getStudent().getFirstName(), + studyRoom.getStudent().getLastName(), + studyRoom.getStudent().getSchool(), + studyRoom.getStudent().getYear(), + studyRoom.getStudent().getPhone(), + studyRoom.getStudent().getParentPhone() + ); + studentRepository.save(nonSignUpStudent); + studyRoom.disconnectStudent(nonSignUpStudent); + } + + public List getStudyRooms(Role role, Long memberId){ + + List studyRoomList; + if(role == Role.TEACHER){ + Teacher teacher = teacherRepository.findById(memberId) + .orElseThrow(() -> new RestApiException(UserErrorCode.TEACHER_NOT_FOUND)); + + studyRoomList = studyRoomRepository.findAllWithStudentByTeacher(teacher); + + } else { + Student student = studentRepository.findById(memberId) + .orElseThrow(() -> new RestApiException(UserErrorCode.STUDENT_NOT_FOUND)); + + studyRoomList = studyRoomRepository.findAllWithTeacherByStudent(student); + } + + List studyRoomResDtoList = studyRoomList.stream().map(studyRoom -> StudyRoomResDto.of(studyRoom, role)).toList(); + return studyRoomResDtoList; + } + + public DetailedStudyRoomResDto getDetailedStudyRoom(Long studyRoomId, Role role){ + + StudyRoom studyRoom; + List scheduleList; + + if(role == Role.TEACHER) { + studyRoom = studyRoomRepository.findWithAllStudyTimeAndStudentById(studyRoomId) + .orElseThrow(() -> new RestApiException(StudyRoomErrorCode.STUDY_ROOM_NOT_FOUND)); + } else { + studyRoom = studyRoomRepository.findWithAllStudyTimeAndTeacherById(studyRoomId) + .orElseThrow(() -> new RestApiException(StudyRoomErrorCode.STUDY_ROOM_NOT_FOUND)); + } + + scheduleList = scheduleRepository.findAllByStudyRoomOrderByDate(studyRoom); + return DetailedStudyRoomResDto.of(studyRoom, scheduleList, role); + } + + // 중복되지 않는 6자리 연결 코드를 생성함 + public Integer generateCode(){ + + int maxAttempts = 10; // 최대 시도 횟수 + + for (int i = 0; i < maxAttempts; i++) { + Integer generatedCode = ThreadLocalRandom.current().nextInt(100000, 1000000); // 6자리 코드 생성 + if (!connectionCodeRepository.existsByCode(generatedCode)) { + return generatedCode; + } + } + + throw new RestApiException(StudyRoomErrorCode.CONNECTION_CODE_GENERATE_FAILED); + } + + public BaseTemplateDto getBaseTemplate(Long studyRoomId) { + + List studyTimeList = studyTimeRepository.findByStudyRoomId(studyRoomId); + if (studyTimeList.isEmpty()) { + throw new RestApiException(StudyRoomErrorCode.STUDY_TIME_NOT_FOUND); + } + + List studyTimeDtoList = new ArrayList<>(); + for (StudyTime st : studyTimeList) { + studyTimeDtoList.add(StudyTimeResDto.of(st)); + } + + StudyRoom studyRoom = studyTimeList.get(0).getStudyRoom(); + + return new BaseTemplateDto(studyRoomId, studyTimeDtoList, studyRoom.getBaseSession(), + studyRoom.getWage()); + } +} diff --git a/src/main/java/turing/turing/domain/studyRoom/dto/request/StudyRoomCreateReqDto.java b/src/main/java/turing/turing/domain/studyRoom/dto/request/StudyRoomCreateReqDto.java index c0d8d3c..1a6e1c5 100644 --- a/src/main/java/turing/turing/domain/studyRoom/dto/request/StudyRoomCreateReqDto.java +++ b/src/main/java/turing/turing/domain/studyRoom/dto/request/StudyRoomCreateReqDto.java @@ -1,6 +1,7 @@ package turing.turing.domain.studyRoom.dto.request; import com.fasterxml.jackson.annotation.JsonFormat; +import jakarta.validation.constraints.NotEmpty; import turing.turing.domain.student.Student; import turing.turing.domain.studyRoom.StudyRoom; import turing.turing.domain.studyTime.dto.StudyTimeReqDto; @@ -17,7 +18,7 @@ public record StudyRoomCreateReqDto( String subject, Integer baseSession, Integer wage, - List studyTimes, + @NotEmpty List studyTimes, @JsonFormat(pattern = "yyyy-MM-dd") LocalDate startDate ) { diff --git a/src/main/java/turing/turing/domain/studyRoom/dto/request/StudyRoomUpdateReqDto.java b/src/main/java/turing/turing/domain/studyRoom/dto/request/StudyRoomUpdateReqDto.java index c8ee9ed..e0241ba 100644 --- a/src/main/java/turing/turing/domain/studyRoom/dto/request/StudyRoomUpdateReqDto.java +++ b/src/main/java/turing/turing/domain/studyRoom/dto/request/StudyRoomUpdateReqDto.java @@ -1,6 +1,8 @@ package turing.turing.domain.studyRoom.dto.request; import com.fasterxml.jackson.annotation.JsonFormat; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; import turing.turing.domain.studyTime.dto.StudyTimeReqDto; import java.time.LocalDate; @@ -8,7 +10,7 @@ public record StudyRoomUpdateReqDto( String subject, - List studyTimes, + @NotEmpty List studyTimes, Integer baseSession, @JsonFormat(pattern = "yyyy-MM-dd") Integer wage diff --git a/src/main/java/turing/turing/domain/studyRoom/dto/response/DetailedStudyRoomResDto.java b/src/main/java/turing/turing/domain/studyRoom/dto/response/DetailedStudyRoomResDto.java index 1174839..c5acb4b 100644 --- a/src/main/java/turing/turing/domain/studyRoom/dto/response/DetailedStudyRoomResDto.java +++ b/src/main/java/turing/turing/domain/studyRoom/dto/response/DetailedStudyRoomResDto.java @@ -33,11 +33,11 @@ public static DetailedStudyRoomResDto of(StudyRoom studyRoom, List sch // 현재 회차 / 총 회차 계산 Schedule schedule = null; - int i = schedules.size() - 1; + int i = schedules.size() - 1; // i를 기준으로 현재 회차를 계산할 것임 while (i >= 0) { schedule = schedules.get(i); LocalDateTime scheduleDateTime = LocalDateTime.of(schedule.getDate(), schedule.getEndTime()); - if(scheduleDateTime.isBefore(LocalDateTime.now())) // 날짜 역순으로 조회하며, 현재 시간보다 과거인 스케줄을 기준으로 현재 회차를 계산 + if(scheduleDateTime.isBefore(LocalDateTime.now())) // 날짜 역순(내림차순)으로 조회하며, 현재 시간보다 과거인 스케줄이 나왔을 때 break break; i--; } @@ -53,13 +53,8 @@ public static DetailedStudyRoomResDto of(StudyRoom studyRoom, List sch role == Role.TEACHER ? studyRoom.getStudent().getSchool() : null, role == Role.TEACHER ? studyRoom.getStudent().getYear() : null, studyRoom.getStudyTimes().stream().map(StudyTimeResDto::of).toList(), - studyRoom.getBaseSession(), - firstSchedule, - studyRoom.getWage(), - curSession, - curBaseSession, - totalSession, - totalBaseSession + studyRoom.getBaseSession(), firstSchedule, studyRoom.getWage(), + curSession, curBaseSession, totalSession, totalBaseSession ); } } diff --git a/src/main/java/turing/turing/domain/studyTime/StudyTimeRepository.java b/src/main/java/turing/turing/domain/studyTime/StudyTimeRepository.java index daba369..3ef0c1c 100644 --- a/src/main/java/turing/turing/domain/studyTime/StudyTimeRepository.java +++ b/src/main/java/turing/turing/domain/studyTime/StudyTimeRepository.java @@ -15,7 +15,7 @@ public interface StudyTimeRepository extends JpaRepository { @Query("select st from StudyTime st " - + "join fetch StudyRoom " - + "where st.studyRoom.id=:studyRoomId") - List findByStudyRoomId(Long studyRoomId); + + "join fetch st.studyRoom sr " + + "where st.studyRoom.id = :studyRoomId") + List findByStudyRoomId(@Param("studyRoomId") Long studyRoomId); } diff --git a/src/main/java/turing/turing/global/exception/errorCode/StudyRoomErrorCode.java b/src/main/java/turing/turing/global/exception/errorCode/StudyRoomErrorCode.java index 721eeae..6479d48 100644 --- a/src/main/java/turing/turing/global/exception/errorCode/StudyRoomErrorCode.java +++ b/src/main/java/turing/turing/global/exception/errorCode/StudyRoomErrorCode.java @@ -14,6 +14,7 @@ public enum StudyRoomErrorCode implements ErrorCode { // ConnectionCode CONNECTION_CODE_NOT_FOUND(HttpStatus.NOT_FOUND, "ConnectionCode Not Found"), ALREADY_CONNECTED_STUDY_ROOM(HttpStatus.BAD_REQUEST, "Already Connected StudyRoom"), + CONNECTION_CODE_GENERATE_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "ConnectionCode Generate Failed (생성 가능한 코드 없음)"), // StudyTime STUDY_TIME_NOT_FOUND(HttpStatus.NOT_FOUND, "StudyTime Not Found"), diff --git a/src/test/java/turing/turing/domain/studyroom/StudyRoomServiceImplTest.java b/src/test/java/turing/turing/domain/studyroom/StudyRoomServiceImplTest.java new file mode 100644 index 0000000..453da7a --- /dev/null +++ b/src/test/java/turing/turing/domain/studyroom/StudyRoomServiceImplTest.java @@ -0,0 +1,389 @@ +package turing.turing.domain.studyroom; + +import com.fasterxml.jackson.annotation.JsonFormat; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.util.ReflectionTestUtils; +import turing.turing.domain.IntegrationTestSupport; +import turing.turing.domain.auth.CustomUserDetails; +import turing.turing.domain.code.ConnectionCode; +import turing.turing.domain.code.ConnectionCodeRepository; +import turing.turing.domain.exam.Exam; +import turing.turing.domain.exam.ExamRepository; +import turing.turing.domain.exam.ExamService; +import turing.turing.domain.exam.dto.CreateExamRequest; +import turing.turing.domain.exam.dto.UpdateExamRequest; +import turing.turing.domain.member.Provider; +import turing.turing.domain.member.Role; +import turing.turing.domain.schedule.Schedule; +import turing.turing.domain.schedule.ScheduleService; +import turing.turing.domain.schedule.dto.ScheduleDto; +import turing.turing.domain.student.Student; +import turing.turing.domain.student.StudentRepository; +import turing.turing.domain.studyRoom.StudyRoom; +import turing.turing.domain.studyRoom.StudyRoomRepository; +import turing.turing.domain.studyRoom.StudyRoomService; +import turing.turing.domain.studyRoom.StudyRoomServiceImpl; +import turing.turing.domain.studyRoom.dto.BaseTemplateDto; +import turing.turing.domain.studyRoom.dto.request.StudyRoomCreateReqDto; +import turing.turing.domain.studyRoom.dto.request.StudyRoomUpdateReqDto; +import turing.turing.domain.studyRoom.dto.response.DetailedStudyRoomResDto; +import turing.turing.domain.studyRoom.dto.response.StudyRoomResDto; +import turing.turing.domain.studyRoom.dto.response.SubjectAndTeacherResDto; +import turing.turing.domain.studyTime.StudyTime; +import turing.turing.domain.studyTime.StudyTimeRepository; +import turing.turing.domain.studyTime.dto.StudyTimeReqDto; +import turing.turing.domain.studyTime.dto.StudyTimeResDto; +import turing.turing.domain.teacher.Teacher; +import turing.turing.domain.teacher.TeacherRepository; +import turing.turing.global.exception.RestApiException; +import turing.turing.global.exception.errorCode.StudyRoomErrorCode; + +import java.time.LocalDate; +import java.time.LocalTime; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.*; + + +@ExtendWith(MockitoExtension.class) +class StudyRoomServiceImplTest extends IntegrationTestSupport{ + + @Autowired + private TeacherRepository teacherRepository; + @Autowired + private StudentRepository studentRepository; + @Autowired + private StudyRoomRepository studyRoomRepository; + @Autowired + private StudyRoomService studyRoomService; + @Autowired + private ExamRepository examRepository; + @Autowired + private ConnectionCodeRepository connectionCodeRepository; + @Autowired + private StudyTimeRepository studyTimeRepository; + + private Teacher teacher; + private Student student; + private StudyRoomCreateReqDto studyRoomCreateReqDto; + + @BeforeEach + void setUp() { + teacher = new Teacher("teacher@naver.com", Role.TEACHER, Provider.KAKAO, "선생", "김"); // 선생님 + teacherRepository.save(teacher); + + ArrayList studyTimes = new ArrayList<>(List.of(new StudyTimeReqDto(1, LocalTime.of(12, 0), LocalTime.of(14, 0)))); + + studyRoomCreateReqDto = new StudyRoomCreateReqDto( // 과외공간 생성 DTO + "학생", "김", "학교", "1", "국어", 8, 20000, studyTimes, LocalDate.of(2020, 1, 1) + ); + } + + @DisplayName("새로운 학생에 대한 과외공간을 추가할 수 있다.") + @Test + void createStudyRoom() { + // given + // (@BeforeEach setUp 메서드에서 수행함) + + // when + Long studyRoomId = studyRoomService.createStudyRoom(teacher.getId(), studyRoomCreateReqDto); + + // then + StudyRoom studyRoom = studyRoomRepository.findById(studyRoomId).get(); + assertThat(studyRoom).extracting("teacher", "linkStatus", "subject", "baseSession", "wage") + .contains(teacher, false, "국어", 8, 20000); // 과외 공간 검증 + assertThat(studyRoom.getStudent()).extracting("firstName", "lastName", "school", "year") + .contains("학생", "김", "학교", "1"); // 임시 학생 검증 + assertThat(studyRoom.getStudyTimes().get(0)).extracting("day", "startTime", "endTime") + .contains(1, LocalTime.of(12, 0), LocalTime.of(14, 0)); // 수업 시간 검증 + } + + @DisplayName("과외공간을 수정할 수 있다.") + @Test + void updateStudyRoom() { + // given + Long studyRoomId = studyRoomService.createStudyRoom(teacher.getId(), studyRoomCreateReqDto); + StudyRoom studyRoom = studyRoomRepository.findById(studyRoomId).get(); + + List newStudyTimes = new ArrayList<>(List.of(new StudyTimeReqDto(2, LocalTime.of(14, 0), LocalTime.of(16, 0)))); + StudyRoomUpdateReqDto studyRoomUpdateReqDto = new StudyRoomUpdateReqDto("수학", newStudyTimes, 4, 30000); + + // when + studyRoomService.updateStudyRoom(studyRoomId, studyRoomUpdateReqDto); + + // then + assertThat(studyRoom).extracting("subject", "baseSession", "wage") + .contains("수학", 4, 30000); // 과외 공간 검증 + assertThat(studyRoom.getStudyTimes().get(0)).extracting("day", "startTime", "endTime") + .contains(2, LocalTime.of(14, 0), LocalTime.of(16, 0)); // 수업 시간 검증 + } + + @DisplayName("과외공간을 삭제할 수 있다.") + @Test + void deleteStudyRoom() { + // given + Long studyRoomId = studyRoomService.createStudyRoom(teacher.getId(), studyRoomCreateReqDto); + + // when + studyRoomService.deleteStudyRoom(studyRoomId); + + // then + Optional studyRoom = studyRoomRepository.findById(studyRoomId); + assertThat(studyRoom).isEmpty(); + } + + @DisplayName("연결 코드를 생성 및 조회할 수 있다.") + @Test + void getConnectionCode() { + // given + Long studyRoomId = studyRoomService.createStudyRoom(teacher.getId(), studyRoomCreateReqDto); + StudyRoom studyRoom = studyRoomRepository.findById(studyRoomId).get(); + + // when + Integer code = studyRoomService.getConnectionCode(studyRoomId); // 연결 코드 생성 + Integer codeAgain = studyRoomService.getConnectionCode(studyRoomId); // 이미 생성된 연결 코드 조회 + + // then + ConnectionCode connectionCode = connectionCodeRepository.findByStudyRoom(studyRoom).get(); + assertThat(code).isEqualTo(connectionCode.getCode()); // 연결 코드 검증 + assertThat(code).isEqualTo(codeAgain); // 조회한 연결 코드 검증 + } + + @DisplayName("연결 코드를 생성 및 조회할 때, 이미 연결된 과외 공간인 경우 예외가 발생한다.") + @Test + void getConnectionCodeFailed() { + // given + Long studyRoomId = studyRoomService.createStudyRoom(teacher.getId(), studyRoomCreateReqDto); + StudyRoom studyRoom = studyRoomRepository.findById(studyRoomId).get(); + Integer code = studyRoomService.getConnectionCode(studyRoomId); // 연결 코드 생성 + + Student student = new Student("student@naver.com", Role.STUDENT, Provider.KAKAO, "학생", "김"); + studentRepository.save(student); // 학생 가입 + + studyRoomService.connectTeacherStudent(student.getId(), code); // 선생님 - 학생 연결 + + // when + // then + assertThatThrownBy(() -> studyRoomService.getConnectionCode(studyRoomId)) + .isInstanceOf(RestApiException.class) + .extracting("errorCode").isEqualTo(StudyRoomErrorCode.ALREADY_CONNECTED_STUDY_ROOM); // 예외 발생 확인 + } + + + @DisplayName("연결 코드를 통해 선생님 정보를 조회할 수 있다.") + @Test + void getTeacherByCode() { + // given + Long studyRoomId = studyRoomService.createStudyRoom(teacher.getId(), studyRoomCreateReqDto); + StudyRoom studyRoom = studyRoomRepository.findById(studyRoomId).get(); + Integer code = studyRoomService.getConnectionCode(studyRoomId); + + // when + SubjectAndTeacherResDto subjectAndTeacherResDto = studyRoomService.getTeacherByCode(code); + + // then + assertThat(subjectAndTeacherResDto).extracting("subject", "teacherFirstName", "teacherLastName") + .contains("국어", "선생", "김"); + } + + @DisplayName("연결 코드를 통해 선생님과 학생을 연결할 수 있다.") + @Test + void connectTeacherStudent() { + // given + Long studyRoomId = studyRoomService.createStudyRoom(teacher.getId(), studyRoomCreateReqDto); + StudyRoom studyRoom = studyRoomRepository.findById(studyRoomId).get(); + Integer code = studyRoomService.getConnectionCode(studyRoomId); + + Student student = new Student("student@naver.com", Role.STUDENT, Provider.KAKAO, "학생", "김"); // 가입 학생 + studentRepository.save(student); + + // when + studyRoomService.connectTeacherStudent(student.getId(), code); + + // then + assertThat(studyRoom.getLinkStatus()).isTrue(); + assertThat(studyRoom.getStudent()).isEqualTo(student); + } + + @DisplayName("선생님과 학생의 연결을 해제할 수 있다.") + @Test + void disconnectTeacherStudent() { + // given + Long studyRoomId = studyRoomService.createStudyRoom(teacher.getId(), studyRoomCreateReqDto); + StudyRoom studyRoom = studyRoomRepository.findById(studyRoomId).get(); + Integer code = studyRoomService.getConnectionCode(studyRoomId); + + Student student = new Student("student@naver.com", Role.STUDENT, Provider.KAKAO, "학생", "김"); // 가입 학생 + studentRepository.save(student); + + studyRoomService.connectTeacherStudent(student.getId(), code); // 연결 + + // when + studyRoomService.disconnectTeacherStudent(studyRoomId); // 연결 해제 + + // then + assertThat(studyRoom.getLinkStatus()).isFalse(); + assertThat(studyRoom.getStudent()).isNotEqualTo(student); // 기존 학생 객체와 달라야 한다 + assertThat(studyRoom.getStudent()).extracting("firstName", "lastName", "school", "year") // 그럼에도 정보는 유지되어야 한다 + .contains("학생", "김", "학교", "1"); + } + + @DisplayName("선생님은 진행 중인 수업을 조회할 수 있다.") + @Test + void getStudyRoomsForTeacher(){ + // given + Long studyRoomId1 = studyRoomService.createStudyRoom(teacher.getId(), studyRoomCreateReqDto); + Long studyRoomId2 = studyRoomService.createStudyRoom(teacher.getId(), studyRoomCreateReqDto); + + // when + List studyRoomResDtoList = studyRoomService.getStudyRooms(Role.TEACHER, teacher.getId()); + + // then + assertThat(studyRoomResDtoList).hasSize(2); + } + + @DisplayName("학생은 진행 중인 수업을 조회할 수 있다.") + @Test + void getStudyRoomsForStudent(){ + // given + Long studyRoomId1 = studyRoomService.createStudyRoom(teacher.getId(), studyRoomCreateReqDto); + Long studyRoomId2 = studyRoomService.createStudyRoom(teacher.getId(), studyRoomCreateReqDto); + Integer code1 = studyRoomService.getConnectionCode(studyRoomId1); + Integer code2 = studyRoomService.getConnectionCode(studyRoomId2); + + Student student = new Student("student@naver.com", Role.STUDENT, Provider.KAKAO, "학생", "김"); // 가입 학생 + studentRepository.save(student); + + studyRoomService.connectTeacherStudent(student.getId(), code1); // 두 과외 공간을 가입 학생과 연결 + studyRoomService.connectTeacherStudent(student.getId(), code2); + + // when + List studyRoomResDtoList = studyRoomService.getStudyRooms(Role.STUDENT, student.getId()); + + // then + assertThat(studyRoomResDtoList).hasSize(2); + } + + @DisplayName("선생님은 진행 중인 수업 상세 정보를 조회할 수 있다.") + @Test + void getDetailedStudyRooms() { + // given + Long studyRoomId = studyRoomService.createStudyRoom(teacher.getId(), studyRoomCreateReqDto); + + // when + DetailedStudyRoomResDto detailedStudyRoom = studyRoomService.getDetailedStudyRoom(studyRoomId, Role.TEACHER); + + // then + assertThat(detailedStudyRoom).extracting("oppositeFirstName", "oppositeLastName", "subject", "baseSession", "wage") + .contains("학생", "김", "국어", 8, 20000); + assertThat(detailedStudyRoom.studyTimes().get(0)).extracting("day", "startTime", "endTime") + .contains(1, LocalTime.of(12, 0), LocalTime.of(14, 0)); + + // TODO: 현재회차 및 총회차 계산 로직 검증 필요 (스케줄 완성 후 추가) + // curSession + // curBaseSession + // totalSession + // totalBaseSession + } + + @DisplayName("학생은 진행 중인 수업 상세 정보를 조회할 수 있다.") + @Test + void getDetailedStudyRoomsForStudent() { + // given + Long studyRoomId = studyRoomService.createStudyRoom(teacher.getId(), studyRoomCreateReqDto); + + Student student = new Student("student@naver.com", Role.STUDENT, Provider.KAKAO, "학생", "김"); // 가입 학생 + studentRepository.save(student); + + studyRoomService.connectTeacherStudent(student.getId(), studyRoomService.getConnectionCode(studyRoomId)); + + // when + DetailedStudyRoomResDto detailedStudyRoom = studyRoomService.getDetailedStudyRoom(studyRoomId, Role.STUDENT); + + // then + assertThat(detailedStudyRoom).extracting("oppositeFirstName", "oppositeLastName", "subject", "baseSession", "wage") + .contains("선생", "김", "국어", 8, 20000); + assertThat(detailedStudyRoom.studyTimes().get(0)).extracting("day", "startTime", "endTime") + .contains(1, LocalTime.of(12, 0), LocalTime.of(14, 0)); + + // TODO: 현재회차 및 총회차 계산 로직 검증 필요 (스케줄 완성 후 추가) + // curSession + // curBaseSession + // totalSession + // totalBaseSession + } + + @DisplayName("중복되지 않는 6자리 연결 코드를 생성할 수 있다.") + @Test + void generateCode() { + // given + // when + Integer code1 = studyRoomService.generateCode(); + Integer code2 = studyRoomService.generateCode(); + + // then + assertThat(code1).isBetween(100000, 999999); // 6자리 코드인지 확인 + assertThat(code1).isNotEqualTo(code2); // 중복되지 않는 코드인지 확인 + } + + @DisplayName("중복되지 않는 6자리 연결 코드 생성을 maxAttempts번 실패하면 예외가 발생한다.") + @Test + void generateCodeFailed() { + // given + ConnectionCodeRepository mockRepository = mock(ConnectionCodeRepository.class); + when(mockRepository.existsByCode(anyInt())).thenReturn(true); // existsByCode가 항상 true를 반환하도록 설정 + ReflectionTestUtils.setField(studyRoomService, "connectionCodeRepository", mockRepository); // 테스트를 위해 mock repository를 주입 + + // when + // then + assertThatThrownBy(() -> studyRoomService.generateCode()) + .isInstanceOf(RestApiException.class) + .extracting("errorCode").isEqualTo(StudyRoomErrorCode.CONNECTION_CODE_GENERATE_FAILED); // 예외 발생 확인 + verify(mockRepository, times(10)).existsByCode(anyInt()); // mock 호출 확인 + + ReflectionTestUtils.setField(studyRoomService, "connectionCodeRepository", connectionCodeRepository); // 원래 repository로 복원 + } + + @DisplayName("과외공간의 기본 템플릿 정보를 조회할 수 있다.") + @Test + void getBaseTemplate() { + // given + Long studyRoomId = studyRoomService.createStudyRoom(teacher.getId(), studyRoomCreateReqDto); + + // when + BaseTemplateDto baseTemplate = studyRoomService.getBaseTemplate(studyRoomId); + + // then + assertThat(baseTemplate).extracting("studyRoomId", "baseSession", "wage") + .contains(studyRoomId, 8, 20000); + assertThat(baseTemplate.getStudyTimeList().get(0)).extracting("day", "startTime", "endTime") + .contains(1, LocalTime.of(12, 0), LocalTime.of(14, 0)); + } + + @DisplayName("과외공간의 기본 템플릿 정보 조회 시, StudyTime이 존재하지 않으면 예외가 발생한다.") + @Test + void getBaseTemplateFailed() { + // given + Long studyRoomId = studyRoomService.createStudyRoom(teacher.getId(), studyRoomCreateReqDto); + studyTimeRepository.deleteByStudyRoomId(studyRoomId); // StudyTime 삭제 + + // when + // then + assertThatThrownBy(() -> studyRoomService.getBaseTemplate(studyRoomId)) // 예외 발생 확인 + .isInstanceOf(RestApiException.class) + .extracting("errorCode").isEqualTo(StudyRoomErrorCode.STUDY_TIME_NOT_FOUND); + } + +} \ No newline at end of file diff --git a/src/test/java/turing/turing/domain/studyroom/StudyRoomTest.java b/src/test/java/turing/turing/domain/studyroom/StudyRoomTest.java new file mode 100644 index 0000000..a8a65c3 --- /dev/null +++ b/src/test/java/turing/turing/domain/studyroom/StudyRoomTest.java @@ -0,0 +1,120 @@ +package turing.turing.domain.studyroom; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.context.annotation.Import; +import turing.turing.domain.exam.Exam; +import turing.turing.domain.member.Provider; +import turing.turing.domain.member.Role; +import turing.turing.domain.student.Student; +import turing.turing.domain.student.StudentRepository; +import turing.turing.domain.studyRoom.StudyRoom; +import turing.turing.domain.studyRoom.StudyRoomRepository; +import turing.turing.domain.teacher.Teacher; +import turing.turing.domain.teacher.TeacherRepository; +import turing.turing.global.config.JpaConfig; + +import java.time.LocalDate; + +import static org.assertj.core.api.Assertions.assertThat; + +@DataJpaTest +@Import(JpaConfig.class) +class StudyRoomTest { + + @Autowired + private TeacherRepository teacherRepository; + @Autowired + private StudentRepository studentRepository; + @Autowired + private StudyRoomRepository studyRoomRepository; + + @DisplayName("학생 연결 상태를 true로 변경할 수 있다.") + @Test + void connectStudentTest() { + //given + Teacher teacher = new Teacher("teacher@naver.com", Role.TEACHER, Provider.KAKAO, "선생", "김"); // 선생님 + Student fakeStudent = new Student("null", Role.STUDENT, Provider.KAKAO, "학생", "임시"); // 미가입 학생 (임시 객체) + StudyRoom studyRoom = new StudyRoom("과목", 8, 20000, teacher, fakeStudent); // 과외 공간 + teacherRepository.save(teacher); + studentRepository.save(fakeStudent); + studyRoomRepository.save(studyRoom); + + Student student = new Student("student@naver.com", Role.STUDENT, Provider.KAKAO, "학생", "김"); // 가입 학생 + studentRepository.save(student); + + //when + studyRoom.connectStudent(student); // 가입한 학생과의 연결 + + //then + assertThat(studyRoom.getLinkStatus()).isEqualTo(true); // 연결 상태 검증 + assertThat(studyRoom.getStudent().getEmail()).isEqualTo("student@naver.com"); // 연결된 학생의 이메일이 맞는지 검증 + } + + @DisplayName("학생 연결 상태를 false로 변경할 수 있다.") + @Test + void disconnectStudentTest() { + //given + Teacher teacher = new Teacher("teacher@naver.com", Role.TEACHER, Provider.KAKAO, "선생", "김"); // 선생님 + Student fakeStudent = new Student("null", Role.STUDENT, Provider.KAKAO, "학생", "임시"); // 미가입 학생 (임시 객체) + StudyRoom studyRoom = new StudyRoom("과목", 8, 20000, teacher, fakeStudent); // 과외 공간 + teacherRepository.save(teacher); + studentRepository.save(fakeStudent); + studyRoomRepository.save(studyRoom); + + Student student = new Student("student@naver.com", Role.STUDENT, Provider.KAKAO, "학생", "김"); // 가입 학생 + studentRepository.save(student); + studyRoom.connectStudent(student); // 가입한 학생과 연결 + + //when + studyRoom.disconnectStudent(fakeStudent); // 학생 연결 시도 (미가입 임시 학생 객체와의 연결) + + //then + assertThat(studyRoom.getLinkStatus()).isEqualTo(false); // 연결 상태 검증 + assertThat(studyRoom.getStudent().getEmail()).isEqualTo("null"); // 임시 학생의 이메일이 맞는지 검증 + } + + @DisplayName("과외 공간의 필드를 변경할 수 있다.") + @Test + void updateStudyRoomTest() { + //given + Teacher teacher = new Teacher("teacher@naver.com", Role.TEACHER, Provider.KAKAO, "선생", "김"); // 선생님 + Student student = new Student("student@naver.com", Role.STUDENT, Provider.KAKAO, "학생", "김"); // 가입 학생 + StudyRoom studyRoom = new StudyRoom("과목", 8, 20000, teacher, student); // 과외 공간 + teacherRepository.save(teacher); + studentRepository.save(student); + studyRoomRepository.save(studyRoom); + studyRoom.connectStudent(student); // 가입한 학생과의 연결 + + //when + studyRoom.updateStudyRoom("새로운 과목", 4, 30000); // 업데이트 시도 + + //then + assertThat(studyRoom.getSubject()).isEqualTo("새로운 과목"); // 연결 상태 검증 + assertThat(studyRoom.getBaseSession()).isEqualTo(4); // 연결 상태 검증 + assertThat(studyRoom.getWage()).isEqualTo(30000); // 연결 상태 검증 + } + + @DisplayName("과외 공간에서 학생 이름을 가져올 수 있다.") + @Test + void getStudentNameTest() { + //given + Teacher teacher = new Teacher("teacher@naver.com", Role.TEACHER, Provider.KAKAO, "선생", "김"); // 선생님 + Student student = new Student("student@naver.com", Role.STUDENT, Provider.KAKAO, "학생", "김"); // 가입 학생 + StudyRoom studyRoom = new StudyRoom("과목", 8, 20000, teacher, student); // 과외 공간 + teacherRepository.save(teacher); + studentRepository.save(student); + studyRoomRepository.save(studyRoom); + studyRoom.connectStudent(student); // 가입한 학생과의 연결 + + //when + String studentName = studyRoom.getStudentName(); + + //then + assertThat(studentName).isEqualTo("김학생"); + } + + +} \ No newline at end of file