diff --git a/src/main/java/in/koreatech/koin/domain/timetableV2/controller/TimetableApiV2.java b/src/main/java/in/koreatech/koin/domain/timetableV2/controller/TimetableApiV2.java index 3c1450fd8..6ee1063c9 100644 --- a/src/main/java/in/koreatech/koin/domain/timetableV2/controller/TimetableApiV2.java +++ b/src/main/java/in/koreatech/koin/domain/timetableV2/controller/TimetableApiV2.java @@ -13,14 +13,13 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestParam; -import in.koreatech.koin.domain.timetableV2.dto.TimeTableLecturesDeleteRequest; -import in.koreatech.koin.domain.timetableV2.dto.TimetableFrameCreateRequest; -import in.koreatech.koin.domain.timetableV2.dto.TimetableFrameResponse; -import in.koreatech.koin.domain.timetableV2.dto.TimetableFrameUpdateRequest; -import in.koreatech.koin.domain.timetableV2.dto.TimetableFrameUpdateResponse; -import in.koreatech.koin.domain.timetableV2.dto.TimetableLectureCreateRequest; -import in.koreatech.koin.domain.timetableV2.dto.TimetableLectureResponse; -import in.koreatech.koin.domain.timetableV2.dto.TimetableLectureUpdateRequest; +import in.koreatech.koin.domain.timetableV2.dto.request.TimetableFrameCreateRequest; +import in.koreatech.koin.domain.timetableV2.dto.request.TimetableFrameUpdateRequest; +import in.koreatech.koin.domain.timetableV2.dto.request.TimetableLectureCreateRequest; +import in.koreatech.koin.domain.timetableV2.dto.request.TimetableLectureUpdateRequest; +import in.koreatech.koin.domain.timetableV2.dto.response.TimetableFrameResponse; +import in.koreatech.koin.domain.timetableV2.dto.response.TimetableFrameUpdateResponse; +import in.koreatech.koin.domain.timetableV2.dto.response.TimetableLectureResponse; import in.koreatech.koin.global.auth.Auth; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.Content; @@ -62,8 +61,8 @@ ResponseEntity createTimetablesFrame( @SecurityRequirement(name = "Jwt Authentication") @PutMapping("/v2/timetables/frame/{id}") ResponseEntity updateTimetableFrame( + @Valid @RequestBody TimetableFrameUpdateRequest request, @PathVariable(value = "id") Integer timetableFrameId, - @Valid @RequestBody TimetableFrameUpdateRequest timetableFrameUpdateRequest, @Auth(permit = {STUDENT}) Integer userId ); @@ -109,7 +108,7 @@ ResponseEntity deleteTimetablesFrame( ) @Operation(summary = "시간표 프레임 모두 삭제") @DeleteMapping("/v2/all/timetables/frame") - public ResponseEntity deleteAllTimetablesFrame( + ResponseEntity deleteAllTimetablesFrame( @RequestParam(name = "semester") String semester, @Auth(permit = {STUDENT}) Integer userId ); @@ -122,11 +121,7 @@ public ResponseEntity deleteAllTimetablesFrame( @ApiResponse(responseCode = "404", content = @Content(schema = @Schema(hidden = true))) } ) - @Operation(summary = "시간표에 강의 정보 추가", - description = """ - lecture_id가 있는 경우 class_title, class_time, professor은 null, grades는 '0'으로 입력해야합니다.\n - lecture_id가 없는 경우 class_title, class_time, professor, grades을 선택적으로 입력합니다. - """) + @Operation(summary = "시간표에 강의 정보 추가") @SecurityRequirement(name = "Jwt Authentication") @PostMapping("/v2/timetables/lecture") ResponseEntity createTimetableLecture( diff --git a/src/main/java/in/koreatech/koin/domain/timetableV2/controller/TimetableControllerV2.java b/src/main/java/in/koreatech/koin/domain/timetableV2/controller/TimetableControllerV2.java index 7d654e3cc..6ae345064 100644 --- a/src/main/java/in/koreatech/koin/domain/timetableV2/controller/TimetableControllerV2.java +++ b/src/main/java/in/koreatech/koin/domain/timetableV2/controller/TimetableControllerV2.java @@ -14,15 +14,15 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import in.koreatech.koin.domain.timetableV2.dto.TimeTableLecturesDeleteRequest; -import in.koreatech.koin.domain.timetableV2.dto.TimetableFrameCreateRequest; -import in.koreatech.koin.domain.timetableV2.dto.TimetableFrameResponse; -import in.koreatech.koin.domain.timetableV2.dto.TimetableFrameUpdateRequest; -import in.koreatech.koin.domain.timetableV2.dto.TimetableFrameUpdateResponse; -import in.koreatech.koin.domain.timetableV2.dto.TimetableLectureCreateRequest; -import in.koreatech.koin.domain.timetableV2.dto.TimetableLectureResponse; -import in.koreatech.koin.domain.timetableV2.dto.TimetableLectureUpdateRequest; -import in.koreatech.koin.domain.timetableV2.service.TimetableServiceV2; +import in.koreatech.koin.domain.timetableV2.dto.request.TimetableFrameCreateRequest; +import in.koreatech.koin.domain.timetableV2.dto.request.TimetableFrameUpdateRequest; +import in.koreatech.koin.domain.timetableV2.dto.request.TimetableLectureCreateRequest; +import in.koreatech.koin.domain.timetableV2.dto.request.TimetableLectureUpdateRequest; +import in.koreatech.koin.domain.timetableV2.dto.response.TimetableFrameResponse; +import in.koreatech.koin.domain.timetableV2.dto.response.TimetableFrameUpdateResponse; +import in.koreatech.koin.domain.timetableV2.dto.response.TimetableLectureResponse; +import in.koreatech.koin.domain.timetableV2.service.TimetableFrameService; +import in.koreatech.koin.domain.timetableV2.service.TimetableLectureService; import in.koreatech.koin.global.auth.Auth; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; @@ -31,26 +31,26 @@ @RequiredArgsConstructor public class TimetableControllerV2 implements TimetableApiV2 { - private final TimetableServiceV2 timetableServiceV2; + private final TimetableFrameService frameServiceV2; + private final TimetableLectureService lectureServiceV2; @PostMapping("/v2/timetables/frame") public ResponseEntity createTimetablesFrame( @Valid @RequestBody TimetableFrameCreateRequest request, @Auth(permit = {STUDENT}) Integer userId ) { - TimetableFrameResponse response = timetableServiceV2.createTimetablesFrame(userId, request); + TimetableFrameResponse response = frameServiceV2.createTimetablesFrame(userId, request); return ResponseEntity.ok(response); } @PutMapping("/v2/timetables/frame/{id}") public ResponseEntity updateTimetableFrame( + @Valid @RequestBody TimetableFrameUpdateRequest request, @PathVariable(value = "id") Integer timetableFrameId, - @Valid @RequestBody TimetableFrameUpdateRequest timetableFrameUpdateRequest, @Auth(permit = {STUDENT}) Integer userId ) { - TimetableFrameUpdateResponse timetableFrameUpdateResponse = - timetableServiceV2.updateTimetableFrame(timetableFrameId, timetableFrameUpdateRequest, userId); - return ResponseEntity.ok(timetableFrameUpdateResponse); + TimetableFrameUpdateResponse response = frameServiceV2.updateTimetableFrame(request, timetableFrameId, userId); + return ResponseEntity.ok(response); } @GetMapping("/v2/timetables/frames") @@ -58,8 +58,8 @@ public ResponseEntity> getTimetablesFrame( @RequestParam(name = "semester") String semester, @Auth(permit = {STUDENT}) Integer userId ) { - List timeTableFrameResponse = timetableServiceV2.getTimetablesFrame(userId, semester); - return ResponseEntity.ok(timeTableFrameResponse); + List response = frameServiceV2.getTimetablesFrame(userId, semester); + return ResponseEntity.ok(response); } @DeleteMapping("/v2/timetables/frame") @@ -67,7 +67,7 @@ public ResponseEntity deleteTimetablesFrame( @RequestParam(name = "id") Integer frameId, @Auth(permit = {STUDENT}) Integer userId ) { - timetableServiceV2.deleteTimetablesFrame(userId, frameId); + frameServiceV2.deleteTimetablesFrame(userId, frameId); return ResponseEntity.noContent().build(); } @@ -76,7 +76,7 @@ public ResponseEntity deleteAllTimetablesFrame( @RequestParam(name = "semester") String semester, @Auth(permit = {STUDENT}) Integer userId ) { - timetableServiceV2.deleteAllTimetablesFrame(userId, semester); + frameServiceV2.deleteAllTimetablesFrame(userId, semester); return ResponseEntity.noContent().build(); } @@ -85,8 +85,8 @@ public ResponseEntity createTimetableLecture( @Valid @RequestBody TimetableLectureCreateRequest request, @Auth(permit = {STUDENT}) Integer userId ) { - TimetableLectureResponse timeTableLectureResponse = timetableServiceV2.createTimetableLectures(userId, request); - return ResponseEntity.ok(timeTableLectureResponse); + TimetableLectureResponse response = lectureServiceV2.createTimetableLectures(userId, request); + return ResponseEntity.ok(response); } @PutMapping("/v2/timetables/lecture") @@ -94,9 +94,8 @@ public ResponseEntity updateTimetableLecture( @Valid @RequestBody TimetableLectureUpdateRequest request, @Auth(permit = {STUDENT}) Integer userId ) { - TimetableLectureResponse timetableLectureResponse = timetableServiceV2.updateTimetablesLectures(userId, - request); - return ResponseEntity.ok(timetableLectureResponse); + TimetableLectureResponse response = lectureServiceV2.updateTimetablesLectures(userId, request); + return ResponseEntity.ok(response); } @GetMapping("/v2/timetables/lecture") @@ -104,9 +103,8 @@ public ResponseEntity getTimetableLecture( @RequestParam(name = "timetable_frame_id") Integer timetableFrameId, @Auth(permit = {STUDENT}) Integer userId ) { - TimetableLectureResponse timetableLectureResponse = timetableServiceV2.getTimetableLectures(userId, - timetableFrameId); - return ResponseEntity.ok(timetableLectureResponse); + TimetableLectureResponse response = lectureServiceV2.getTimetableLectures(userId, timetableFrameId); + return ResponseEntity.ok(response); } @DeleteMapping("/v2/timetables/lecture/{id}") @@ -114,7 +112,7 @@ public ResponseEntity deleteTimetableLecture( @PathVariable(value = "id") Integer timetableLectureId, @Auth(permit = {STUDENT}) Integer userId ) { - timetableServiceV2.deleteTimetableLecture(userId, timetableLectureId); + lectureServiceV2.deleteTimetableLecture(userId, timetableLectureId); return ResponseEntity.noContent().build(); } @@ -123,7 +121,7 @@ public ResponseEntity deleteTimetableLectures( @RequestParam(name = "timetable_lecture_ids") List request, @Auth(permit = {STUDENT}) Integer userId ) { - timetableServiceV2.deleteTimetableLectures(request, userId); + lectureServiceV2.deleteTimetableLectures(request, userId); return ResponseEntity.noContent().build(); } @@ -133,7 +131,7 @@ public ResponseEntity deleteTimetableLectureByFrameId( @PathVariable(value = "lectureId") Integer lectureId, @Auth(permit = {STUDENT}) Integer userId ) { - timetableServiceV2.deleteTimetableLectureByFrameId(frameId, lectureId, userId); + lectureServiceV2.deleteTimetableLectureByFrameId(frameId, lectureId, userId); return ResponseEntity.noContent().build(); } } diff --git a/src/main/java/in/koreatech/koin/domain/timetableV2/dto/TimeTableLecturesDeleteRequest.java b/src/main/java/in/koreatech/koin/domain/timetableV2/dto/TimeTableLecturesDeleteRequest.java deleted file mode 100644 index 15942ae2f..000000000 --- a/src/main/java/in/koreatech/koin/domain/timetableV2/dto/TimeTableLecturesDeleteRequest.java +++ /dev/null @@ -1,13 +0,0 @@ -package in.koreatech.koin.domain.timetableV2.dto; - -import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; - -import java.util.List; - -import io.swagger.v3.oas.annotations.media.Schema; - -public record TimeTableLecturesDeleteRequest( - @Schema(description = "timetableLecture id 리스트", example = "[1, 2, 3]", requiredMode = REQUIRED) - List timetablesLectureIds -) { -} diff --git a/src/main/java/in/koreatech/koin/domain/timetableV2/dto/TimetableFrameCreateRequest.java b/src/main/java/in/koreatech/koin/domain/timetableV2/dto/request/TimetableFrameCreateRequest.java similarity index 92% rename from src/main/java/in/koreatech/koin/domain/timetableV2/dto/TimetableFrameCreateRequest.java rename to src/main/java/in/koreatech/koin/domain/timetableV2/dto/request/TimetableFrameCreateRequest.java index f52ed5046..893272c4c 100644 --- a/src/main/java/in/koreatech/koin/domain/timetableV2/dto/TimetableFrameCreateRequest.java +++ b/src/main/java/in/koreatech/koin/domain/timetableV2/dto/request/TimetableFrameCreateRequest.java @@ -1,4 +1,4 @@ -package in.koreatech.koin.domain.timetableV2.dto; +package in.koreatech.koin.domain.timetableV2.dto.request; import static com.fasterxml.jackson.databind.PropertyNamingStrategies.*; import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.NOT_REQUIRED; @@ -12,7 +12,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; -@JsonNaming(SnakeCaseStrategy.class) +@JsonNaming(value = SnakeCaseStrategy.class) public record TimetableFrameCreateRequest( @Schema(description = "학기 정보", example = "20192", requiredMode = REQUIRED) @NotBlank(message = "학기 정보를 입력해주세요") diff --git a/src/main/java/in/koreatech/koin/domain/timetableV2/dto/TimetableFrameUpdateRequest.java b/src/main/java/in/koreatech/koin/domain/timetableV2/dto/request/TimetableFrameUpdateRequest.java similarity index 94% rename from src/main/java/in/koreatech/koin/domain/timetableV2/dto/TimetableFrameUpdateRequest.java rename to src/main/java/in/koreatech/koin/domain/timetableV2/dto/request/TimetableFrameUpdateRequest.java index 1ef01920d..a9d8d9b5f 100644 --- a/src/main/java/in/koreatech/koin/domain/timetableV2/dto/TimetableFrameUpdateRequest.java +++ b/src/main/java/in/koreatech/koin/domain/timetableV2/dto/request/TimetableFrameUpdateRequest.java @@ -1,4 +1,4 @@ -package in.koreatech.koin.domain.timetableV2.dto; +package in.koreatech.koin.domain.timetableV2.dto.request; import static com.fasterxml.jackson.databind.PropertyNamingStrategies.SnakeCaseStrategy; import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; diff --git a/src/main/java/in/koreatech/koin/domain/timetableV2/dto/TimetableLectureCreateRequest.java b/src/main/java/in/koreatech/koin/domain/timetableV2/dto/request/TimetableLectureCreateRequest.java similarity index 98% rename from src/main/java/in/koreatech/koin/domain/timetableV2/dto/TimetableLectureCreateRequest.java rename to src/main/java/in/koreatech/koin/domain/timetableV2/dto/request/TimetableLectureCreateRequest.java index 644c4c97c..a4fbddc09 100644 --- a/src/main/java/in/koreatech/koin/domain/timetableV2/dto/TimetableLectureCreateRequest.java +++ b/src/main/java/in/koreatech/koin/domain/timetableV2/dto/request/TimetableLectureCreateRequest.java @@ -1,4 +1,4 @@ -package in.koreatech.koin.domain.timetableV2.dto; +package in.koreatech.koin.domain.timetableV2.dto.request; import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.NOT_REQUIRED; import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; diff --git a/src/main/java/in/koreatech/koin/domain/timetableV2/dto/TimetableLectureUpdateRequest.java b/src/main/java/in/koreatech/koin/domain/timetableV2/dto/request/TimetableLectureUpdateRequest.java similarity index 97% rename from src/main/java/in/koreatech/koin/domain/timetableV2/dto/TimetableLectureUpdateRequest.java rename to src/main/java/in/koreatech/koin/domain/timetableV2/dto/request/TimetableLectureUpdateRequest.java index 93ee27df5..98fa0e2a2 100644 --- a/src/main/java/in/koreatech/koin/domain/timetableV2/dto/TimetableLectureUpdateRequest.java +++ b/src/main/java/in/koreatech/koin/domain/timetableV2/dto/request/TimetableLectureUpdateRequest.java @@ -1,4 +1,4 @@ -package in.koreatech.koin.domain.timetableV2.dto; +package in.koreatech.koin.domain.timetableV2.dto.request; import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.NOT_REQUIRED; import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; diff --git a/src/main/java/in/koreatech/koin/domain/timetableV2/dto/TimetableFrameResponse.java b/src/main/java/in/koreatech/koin/domain/timetableV2/dto/response/TimetableFrameResponse.java similarity index 94% rename from src/main/java/in/koreatech/koin/domain/timetableV2/dto/TimetableFrameResponse.java rename to src/main/java/in/koreatech/koin/domain/timetableV2/dto/response/TimetableFrameResponse.java index 1a2403f78..3f47b31b8 100644 --- a/src/main/java/in/koreatech/koin/domain/timetableV2/dto/TimetableFrameResponse.java +++ b/src/main/java/in/koreatech/koin/domain/timetableV2/dto/response/TimetableFrameResponse.java @@ -1,4 +1,4 @@ -package in.koreatech.koin.domain.timetableV2.dto; +package in.koreatech.koin.domain.timetableV2.dto.response; import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; diff --git a/src/main/java/in/koreatech/koin/domain/timetableV2/dto/TimetableFrameUpdateResponse.java b/src/main/java/in/koreatech/koin/domain/timetableV2/dto/response/TimetableFrameUpdateResponse.java similarity index 90% rename from src/main/java/in/koreatech/koin/domain/timetableV2/dto/TimetableFrameUpdateResponse.java rename to src/main/java/in/koreatech/koin/domain/timetableV2/dto/response/TimetableFrameUpdateResponse.java index 6a13cd730..12ed33332 100644 --- a/src/main/java/in/koreatech/koin/domain/timetableV2/dto/TimetableFrameUpdateResponse.java +++ b/src/main/java/in/koreatech/koin/domain/timetableV2/dto/response/TimetableFrameUpdateResponse.java @@ -1,4 +1,4 @@ -package in.koreatech.koin.domain.timetableV2.dto; +package in.koreatech.koin.domain.timetableV2.dto.response; import static com.fasterxml.jackson.databind.PropertyNamingStrategies.SnakeCaseStrategy; import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; @@ -19,11 +19,11 @@ public record TimetableFrameUpdateResponse( @Schema(description = "메인 시간표 여부", example = "false", requiredMode = REQUIRED) Boolean isMain ) { - public static TimetableFrameUpdateResponse from(TimetableFrame timetableFrame) { return new TimetableFrameUpdateResponse( timetableFrame.getId(), timetableFrame.getName(), - timetableFrame.isMain()); + timetableFrame.isMain() + ); } } diff --git a/src/main/java/in/koreatech/koin/domain/timetableV2/dto/TimetableLectureResponse.java b/src/main/java/in/koreatech/koin/domain/timetableV2/dto/response/TimetableLectureResponse.java similarity index 61% rename from src/main/java/in/koreatech/koin/domain/timetableV2/dto/TimetableLectureResponse.java rename to src/main/java/in/koreatech/koin/domain/timetableV2/dto/response/TimetableLectureResponse.java index 04e9dcefa..8621f4ce4 100644 --- a/src/main/java/in/koreatech/koin/domain/timetableV2/dto/TimetableLectureResponse.java +++ b/src/main/java/in/koreatech/koin/domain/timetableV2/dto/response/TimetableLectureResponse.java @@ -1,17 +1,18 @@ -package in.koreatech.koin.domain.timetableV2.dto; +package in.koreatech.koin.domain.timetableV2.dto.response; import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.NOT_REQUIRED; import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.List; import java.util.Objects; import com.fasterxml.jackson.databind.PropertyNamingStrategies.SnakeCaseStrategy; import com.fasterxml.jackson.databind.annotation.JsonNaming; +import in.koreatech.koin.domain.timetable.model.Lecture; +import in.koreatech.koin.domain.timetableV2.model.TimetableFrame; import in.koreatech.koin.domain.timetableV2.model.TimetableLecture; import io.swagger.v3.oas.annotations.media.Schema; @@ -78,15 +79,17 @@ public static List from(List ti List timetableLectureList = new ArrayList<>(); for (TimetableLecture timetableLecture : timetableLectures) { + Lecture lecture = timetableLecture.getLecture(); InnerTimetableLectureResponse response; - if (timetableLecture.getLecture() == null) { + + if (lecture == null) { response = new InnerTimetableLectureResponse( timetableLecture.getId(), null, null, null, null, - parseIntegerClassTimesFromString(timetableLecture.getClassTime()), + parseClassTimes(timetableLecture.getClassTime()), timetableLecture.getClassPlace(), timetableLecture.getMemo(), timetableLecture.getGrades(), @@ -99,55 +102,74 @@ public static List from(List ti } else { response = new InnerTimetableLectureResponse( timetableLecture.getId(), - timetableLecture.getLecture().getId(), - timetableLecture.getLecture().getRegularNumber(), - timetableLecture.getLecture().getCode(), - timetableLecture.getLecture().getDesignScore(), - timetableLecture.getClassTime() == null - ? parseIntegerClassTimesFromString( - timetableLecture.getLecture().getClassTime()) - : parseIntegerClassTimesFromString(timetableLecture.getClassTime()), + lecture.getId(), + lecture.getRegularNumber(), + lecture.getCode(), + lecture.getDesignScore(), + getClassTime(timetableLecture, lecture), timetableLecture.getClassPlace(), timetableLecture.getMemo(), - Objects.equals(timetableLecture.getGrades(), "0") - ? timetableLecture.getLecture().getGrades() - : timetableLecture.getGrades(), - timetableLecture.getClassTitle() == null - ? timetableLecture.getLecture().getName() - : timetableLecture.getClassTitle(), - timetableLecture.getLecture().getLectureClass(), - timetableLecture.getLecture().getTarget(), - timetableLecture.getProfessor() == null - ? timetableLecture.getLecture().getProfessor() - : timetableLecture.getProfessor(), - timetableLecture.getLecture().getDepartment() + getGrades(timetableLecture, lecture), + getClassTitle(timetableLecture, lecture), + lecture.getLectureClass(), + lecture.getTarget(), + getProfessor(timetableLecture, lecture), + lecture.getDepartment() ); } timetableLectureList.add(response); } return timetableLectureList; } - } - public static TimetableLectureResponse of(Integer timetableFrameId, List timetableLectures, - Integer grades, Integer totalGrades) { - return new TimetableLectureResponse(timetableFrameId, InnerTimetableLectureResponse.from(timetableLectures), - grades, totalGrades); + private static String getProfessor(TimetableLecture timetableLecture, Lecture lecture) { + if (timetableLecture.getProfessor() == null) { + return lecture.getProfessor(); + } + return timetableLecture.getProfessor(); + } + + private static String getClassTitle(TimetableLecture timetableLecture, Lecture lecture) { + if (timetableLecture.getClassTitle() == null) { + return lecture.getName(); + } + return timetableLecture.getClassTitle(); + } + + private static String getGrades(TimetableLecture timetableLecture, Lecture lecture) { + if (Objects.equals(timetableLecture.getGrades(), GRADE_ZERO)) { + return lecture.getGrades(); + } + return timetableLecture.getGrades(); + } + + private static List getClassTime(TimetableLecture timetableLecture, Lecture lecture) { + if (timetableLecture.getClassTime() == null) { + return parseClassTimes(lecture.getClassTime()); + } + return parseClassTimes(timetableLecture.getClassTime()); + } } + private static final String GRADE_ZERO = "0"; private static final int INITIAL_BRACE_INDEX = 1; + private static final String SEPARATOR = ","; + + public static TimetableLectureResponse of(TimetableFrame timetableFrame, Integer grades, Integer totalGrades) { + return new TimetableLectureResponse( + timetableFrame.getId(), + InnerTimetableLectureResponse.from(timetableFrame.getTimetableLectures()), + grades, + totalGrades + ); + } - private static List parseIntegerClassTimesFromString(String classTime) { + private static List parseClassTimes(String classTime) { String classTimeWithoutBrackets = classTime.substring(INITIAL_BRACE_INDEX, classTime.length() - 1); - if (!classTimeWithoutBrackets.isEmpty()) { - return Arrays.stream(classTimeWithoutBrackets.split(",")) - .map(String::strip) - .map(Integer::parseInt) - .toList(); - } else { - return Collections.emptyList(); - } + return Arrays.stream(classTimeWithoutBrackets.split(SEPARATOR)) + .map(String::strip) + .map(Integer::parseInt) + .toList(); } } - diff --git a/src/main/java/in/koreatech/koin/domain/timetableV2/factory/TimetableFrameCreator.java b/src/main/java/in/koreatech/koin/domain/timetableV2/factory/TimetableFrameCreator.java new file mode 100644 index 000000000..e07e61222 --- /dev/null +++ b/src/main/java/in/koreatech/koin/domain/timetableV2/factory/TimetableFrameCreator.java @@ -0,0 +1,37 @@ +package in.koreatech.koin.domain.timetableV2.factory; + +import org.springframework.stereotype.Component; + +import in.koreatech.koin.domain.timetable.model.Semester; +import in.koreatech.koin.domain.timetableV2.dto.request.TimetableFrameCreateRequest; +import in.koreatech.koin.domain.timetableV2.model.TimetableFrame; +import in.koreatech.koin.domain.user.model.User; + +@Component +public class TimetableFrameCreator { + + private static final String DEFAULT_TIMETABLE_FRAME_NAME = "시간표"; + + public TimetableFrame createTimetableFrame( + TimetableFrameCreateRequest request, User user, Semester semester, int currentFrameCount + ) { + boolean isMain = determineIfMain(currentFrameCount); + String name = determineTimetableName(request.timetableName(), currentFrameCount); + return request.toTimetablesFrame(user, semester, name, isMain); + } + + private boolean determineIfMain(int currentFrameCount) { + return currentFrameCount == 0; + } + + private String determineTimetableName(String requestedName, int currentFrameCount) { + if (requestedName != null) { + return requestedName; + } + return getDefaultTimetableFrameName(currentFrameCount + 1); + } + + private String getDefaultTimetableFrameName(int currentFrameCount) { + return DEFAULT_TIMETABLE_FRAME_NAME + (currentFrameCount); + } +} diff --git a/src/main/java/in/koreatech/koin/domain/timetableV2/factory/TimetableFrameUpdater.java b/src/main/java/in/koreatech/koin/domain/timetableV2/factory/TimetableFrameUpdater.java new file mode 100644 index 000000000..2dbb17883 --- /dev/null +++ b/src/main/java/in/koreatech/koin/domain/timetableV2/factory/TimetableFrameUpdater.java @@ -0,0 +1,31 @@ +package in.koreatech.koin.domain.timetableV2.factory; + +import org.springframework.stereotype.Component; + +import in.koreatech.koin.domain.timetableV2.dto.response.TimetableFrameUpdateResponse; +import in.koreatech.koin.domain.timetableV2.model.TimetableFrame; +import in.koreatech.koin.domain.timetableV2.repository.TimetableFrameRepositoryV2; +import lombok.RequiredArgsConstructor; + +@Component +@RequiredArgsConstructor +public class TimetableFrameUpdater { + + private final TimetableFrameRepositoryV2 timetableFrameRepositoryV2; + + public TimetableFrameUpdateResponse updateTimetableFrame( + TimetableFrame timeTableFrame, Integer userId, String timetableName, boolean isMain + ) { + cancelIfMainTimetable(userId, timeTableFrame.getSemester().getId(), isMain); + timeTableFrame.updateTimetableFrame(timeTableFrame.getSemester(), timetableName, isMain); + return TimetableFrameUpdateResponse.from(timeTableFrame); + } + + private void cancelIfMainTimetable(Integer userId, Integer semesterId, boolean isMain) { + if (isMain) { + TimetableFrame mainTimetableFrame = timetableFrameRepositoryV2.getMainTimetableByUserIdAndSemesterId(userId, + semesterId); + mainTimetableFrame.updateMainFlag(false); + } + } +} diff --git a/src/main/java/in/koreatech/koin/domain/timetableV2/factory/TimetableLectureCreator.java b/src/main/java/in/koreatech/koin/domain/timetableV2/factory/TimetableLectureCreator.java new file mode 100644 index 000000000..645345d44 --- /dev/null +++ b/src/main/java/in/koreatech/koin/domain/timetableV2/factory/TimetableLectureCreator.java @@ -0,0 +1,37 @@ +package in.koreatech.koin.domain.timetableV2.factory; + +import static in.koreatech.koin.domain.timetableV2.dto.request.TimetableLectureCreateRequest.*; + +import org.springframework.stereotype.Component; + +import in.koreatech.koin.domain.timetable.model.Lecture; +import in.koreatech.koin.domain.timetableV2.dto.request.TimetableLectureCreateRequest; +import in.koreatech.koin.domain.timetableV2.model.TimetableFrame; +import in.koreatech.koin.domain.timetableV2.model.TimetableLecture; +import in.koreatech.koin.domain.timetableV2.repository.LectureRepositoryV2; +import in.koreatech.koin.domain.timetableV2.repository.TimetableLectureRepositoryV2; +import lombok.RequiredArgsConstructor; + +@Component +@RequiredArgsConstructor +public class TimetableLectureCreator { + + private final LectureRepositoryV2 lectureRepositoryV2; + private final TimetableLectureRepositoryV2 timetableLectureRepositoryV2; + + public void createTimetableLectures(TimetableLectureCreateRequest request, TimetableFrame frame) { + for (InnerTimeTableLectureRequest lectureRequest : request.timetableLecture()) { + Lecture lecture = determineLecture(lectureRequest.lectureId()); + TimetableLecture timetableLecture = lectureRequest.toTimetableLecture(frame, lecture); + frame.addTimeTableLecture(timetableLecture); + timetableLectureRepositoryV2.save(timetableLecture); + } + } + + private Lecture determineLecture(Integer lectureId) { + if (lectureId != null) { + return lectureRepositoryV2.getLectureById(lectureId); + } + return null; + } +} diff --git a/src/main/java/in/koreatech/koin/domain/timetableV2/factory/TimetableLectureUpdater.java b/src/main/java/in/koreatech/koin/domain/timetableV2/factory/TimetableLectureUpdater.java new file mode 100644 index 000000000..2f5aabb3d --- /dev/null +++ b/src/main/java/in/koreatech/koin/domain/timetableV2/factory/TimetableLectureUpdater.java @@ -0,0 +1,31 @@ +package in.koreatech.koin.domain.timetableV2.factory; + +import static in.koreatech.koin.domain.timetableV2.dto.request.TimetableLectureUpdateRequest.InnerTimetableLectureRequest; + +import org.springframework.stereotype.Component; + +import in.koreatech.koin.domain.timetableV2.dto.request.TimetableLectureUpdateRequest; +import in.koreatech.koin.domain.timetableV2.model.TimetableLecture; +import in.koreatech.koin.domain.timetableV2.repository.TimetableLectureRepositoryV2; +import lombok.RequiredArgsConstructor; + +@Component +@RequiredArgsConstructor +public class TimetableLectureUpdater { + + private final TimetableLectureRepositoryV2 timetableLectureRepositoryV2; + + public void updateTimetablesLectures(TimetableLectureUpdateRequest request) { + for (InnerTimetableLectureRequest timetableRequest : request.timetableLecture()) { + TimetableLecture timetableLecture = timetableLectureRepositoryV2.getById(timetableRequest.id()); + timetableLecture.update( + timetableRequest.classTitle(), + timetableRequest.classTime().toString(), + timetableRequest.classPlace(), + timetableRequest.professor(), + timetableRequest.grades(), + timetableRequest.memo() + ); + } + } +} diff --git a/src/main/java/in/koreatech/koin/domain/timetableV2/model/TimetableFrame.java b/src/main/java/in/koreatech/koin/domain/timetableV2/model/TimetableFrame.java index fc5b2fce7..d06304c9a 100644 --- a/src/main/java/in/koreatech/koin/domain/timetableV2/model/TimetableFrame.java +++ b/src/main/java/in/koreatech/koin/domain/timetableV2/model/TimetableFrame.java @@ -4,6 +4,7 @@ import static jakarta.persistence.GenerationType.IDENTITY; import static lombok.AccessLevel.PROTECTED; +import java.util.ArrayList; import java.util.List; import org.hibernate.annotations.Where; @@ -67,12 +68,8 @@ public class TimetableFrame extends BaseEntity { @OneToMany(mappedBy = "timetableFrame", orphanRemoval = true, cascade = ALL) private List timetableLectures; - public void updateStatusMain(boolean isMain) { - this.isMain = isMain; - } - @Builder - private TimetableFrame( + public TimetableFrame( User user, Semester semester, String name, @@ -94,7 +91,14 @@ public void updateTimetableFrame(Semester semester, String name, boolean isMain) this.isMain = isMain; } - public void cancelMain() { - isMain = false; + public void updateMainFlag(boolean isMain) { + this.isMain = isMain; + } + + public void addTimeTableLecture(TimetableLecture lecture) { + if (timetableLectures == null) { + timetableLectures = new ArrayList<>(); + } + timetableLectures.add(lecture); } } diff --git a/src/main/java/in/koreatech/koin/domain/timetableV2/model/TimetableLecture.java b/src/main/java/in/koreatech/koin/domain/timetableV2/model/TimetableLecture.java index 0fc1571bd..26b3ddb92 100644 --- a/src/main/java/in/koreatech/koin/domain/timetableV2/model/TimetableLecture.java +++ b/src/main/java/in/koreatech/koin/domain/timetableV2/model/TimetableLecture.java @@ -85,8 +85,11 @@ public TimetableLecture(String classTitle, String classTime, String classPlace, this.timetableFrame = timetableFrame; } - public void update(String classTitle, String classTime, String classPlace, String professor, - String grades, String memo) { + public void update( + String classTitle, String classTime, + String classPlace, String professor, + String grades, String memo + ) { this.classTitle = classTitle; this.classTime = classTime; this.classPlace = classPlace; diff --git a/src/main/java/in/koreatech/koin/domain/timetableV2/repository/SemesterRepositoryV2.java b/src/main/java/in/koreatech/koin/domain/timetableV2/repository/SemesterRepositoryV2.java index ee3e1367c..40f48f866 100644 --- a/src/main/java/in/koreatech/koin/domain/timetableV2/repository/SemesterRepositoryV2.java +++ b/src/main/java/in/koreatech/koin/domain/timetableV2/repository/SemesterRepositoryV2.java @@ -1,6 +1,5 @@ package in.koreatech.koin.domain.timetableV2.repository; -import java.util.List; import java.util.Optional; import org.springframework.data.repository.Repository; diff --git a/src/main/java/in/koreatech/koin/domain/timetableV2/repository/TimetableFrameRepositoryV2.java b/src/main/java/in/koreatech/koin/domain/timetableV2/repository/TimetableFrameRepositoryV2.java index 5dbc6ceb1..b696e4313 100644 --- a/src/main/java/in/koreatech/koin/domain/timetableV2/repository/TimetableFrameRepositoryV2.java +++ b/src/main/java/in/koreatech/koin/domain/timetableV2/repository/TimetableFrameRepositoryV2.java @@ -20,10 +20,12 @@ public interface TimetableFrameRepositoryV2 extends Repository findById(Integer id); @Lock(LockModeType.PESSIMISTIC_WRITE) - @Query("SELECT t FROM TimetableFrame t WHERE t.id = :id") + @Query(""" + SELECT t FROM TimetableFrame t + WHERE t.id = :id + """) Optional findByIdWithLock(@Param("id") Integer id); - List findByUserIdAndIsMainTrue(Integer userId); Optional findByUserIdAndSemesterIdAndIsMainTrue(Integer userId, Integer semesterId); @@ -35,7 +37,7 @@ default TimetableFrame getById(Integer id) { default TimetableFrame getByIdWithLock(Integer id) { return findByIdWithLock(id) - .orElseThrow(() -> TimetableNotFoundException.withDetail("id: " + id)); + .orElseThrow(() -> TimetableNotFoundException.withDetail("id: " + id)); } default TimetableFrame getMainTimetableByUserIdAndSemesterId(Integer userId, Integer semesterId) { @@ -56,7 +58,16 @@ default TimetableFrame getByUser(User user) { } @Lock(LockModeType.PESSIMISTIC_WRITE) - TimetableFrame findFirstByUserIdAndSemesterIdAndIsMainFalseOrderByCreatedAtAsc(Integer userId, Integer semesterId); + @Query( + """ + SELECT t FROM TimetableFrame t + WHERE t.user.id = :userId + AND t.semester.id = :semesterId + AND t.isMain = false + ORDER BY t.createdAt ASC + LIMIT 1 + """) + TimetableFrame findNextFirstTimetableFrame(@Param("userId") Integer userId, @Param("semesterId") Integer semesterId); @Query( """ diff --git a/src/main/java/in/koreatech/koin/domain/timetableV2/repository/TimetableLectureRepositoryV2.java b/src/main/java/in/koreatech/koin/domain/timetableV2/repository/TimetableLectureRepositoryV2.java index 56ca4d87a..fc09b2ebb 100644 --- a/src/main/java/in/koreatech/koin/domain/timetableV2/repository/TimetableLectureRepositoryV2.java +++ b/src/main/java/in/koreatech/koin/domain/timetableV2/repository/TimetableLectureRepositoryV2.java @@ -15,7 +15,7 @@ public interface TimetableLectureRepositoryV2 extends Repository findById(Integer id); - List findAllByTimetableFrameId(Integer id); + List findAllByTimetableFrameId(Integer frameId); void deleteById(Integer id); diff --git a/src/main/java/in/koreatech/koin/domain/timetableV2/service/TimetableFrameService.java b/src/main/java/in/koreatech/koin/domain/timetableV2/service/TimetableFrameService.java new file mode 100644 index 000000000..028adb70e --- /dev/null +++ b/src/main/java/in/koreatech/koin/domain/timetableV2/service/TimetableFrameService.java @@ -0,0 +1,93 @@ +package in.koreatech.koin.domain.timetableV2.service; + +import static in.koreatech.koin.domain.timetableV2.validation.TimetableFrameValidate.validateTimetableFrameUpdate; +import static in.koreatech.koin.domain.timetableV2.validation.TimetableFrameValidate.validateUserAuthorization; + +import java.util.List; +import java.util.Objects; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import in.koreatech.koin.domain.timetable.model.Semester; +import in.koreatech.koin.domain.timetableV2.dto.request.TimetableFrameCreateRequest; +import in.koreatech.koin.domain.timetableV2.dto.request.TimetableFrameUpdateRequest; +import in.koreatech.koin.domain.timetableV2.dto.response.TimetableFrameResponse; +import in.koreatech.koin.domain.timetableV2.dto.response.TimetableFrameUpdateResponse; +import in.koreatech.koin.domain.timetableV2.model.TimetableFrame; +import in.koreatech.koin.domain.timetableV2.repository.SemesterRepositoryV2; +import in.koreatech.koin.domain.timetableV2.repository.TimetableFrameRepositoryV2; +import in.koreatech.koin.domain.timetableV2.factory.TimetableFrameCreator; +import in.koreatech.koin.domain.timetableV2.factory.TimetableFrameUpdater; +import in.koreatech.koin.domain.user.model.User; +import in.koreatech.koin.domain.user.repository.UserRepository; +import in.koreatech.koin.global.auth.exception.AuthorizationException; +import in.koreatech.koin.global.concurrent.ConcurrencyGuard; +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class TimetableFrameService { + + private final TimetableFrameRepositoryV2 timetableFrameRepositoryV2; + private final UserRepository userRepository; + private final SemesterRepositoryV2 semesterRepositoryV2; + private final TimetableFrameCreator timetableFrameCreator; + private final TimetableFrameUpdater timetableFrameUpdater; + + @Transactional + public TimetableFrameResponse createTimetablesFrame(Integer userId, TimetableFrameCreateRequest request) { + Semester semester = semesterRepositoryV2.getBySemester(request.semester()); + User user = userRepository.getById(userId); + int currentFrameCount = timetableFrameRepositoryV2.countByUserIdAndSemesterId(userId, semester.getId()); + + TimetableFrame frame = timetableFrameCreator.createTimetableFrame(request, user, semester, currentFrameCount); + TimetableFrame saveFrame = timetableFrameRepositoryV2.save(frame); + + return TimetableFrameResponse.from(saveFrame); + } + + @Transactional + public TimetableFrameUpdateResponse updateTimetableFrame( + TimetableFrameUpdateRequest request, Integer timetableFrameId, Integer userId + ) { + TimetableFrame frame = timetableFrameRepositoryV2.getById(timetableFrameId); + validateTimetableFrameUpdate(frame, request.isMain()); + return timetableFrameUpdater.updateTimetableFrame(frame, userId, request.timetableName(), request.isMain()); + } + + public List getTimetablesFrame(Integer userId, String semesterRequest) { + Semester semester = semesterRepositoryV2.getBySemester(semesterRequest); + return timetableFrameRepositoryV2.findAllByUserIdAndSemesterId(userId, semester.getId()).stream() + .map(TimetableFrameResponse::from) + .toList(); + } + + @Transactional + public void deleteAllTimetablesFrame(Integer userId, String semester) { + User user = userRepository.getById(userId); + Semester timetableSemester = semesterRepositoryV2.getBySemester(semester); + timetableFrameRepositoryV2.deleteAllByUserAndSemester(user, timetableSemester); + } + + @Transactional + public void deleteTimetablesFrame(Integer userId, Integer frameId) { + TimetableFrame timetableFrame = timetableFrameRepositoryV2.getByIdWithLock(frameId); + validateUserAuthorization(timetableFrame.getUser().getId(), userId); + + deleteFrameAndUpdateMainStatusWithLock(frameId, userId, timetableFrame); + } + + @ConcurrencyGuard(lockName = "deleteFrame") + private void deleteFrameAndUpdateMainStatusWithLock(Integer frameId, Integer userId, TimetableFrame frame) { + timetableFrameRepositoryV2.deleteById(frameId); + if (frame.isMain()) { + TimetableFrame nextFrame = timetableFrameRepositoryV2.findNextFirstTimetableFrame(userId, + frame.getSemester().getId()); + if (nextFrame != null) { + nextFrame.updateMainFlag(true); + } + } + } +} diff --git a/src/main/java/in/koreatech/koin/domain/timetableV2/service/TimetableLectureService.java b/src/main/java/in/koreatech/koin/domain/timetableV2/service/TimetableLectureService.java new file mode 100644 index 000000000..e75f02ede --- /dev/null +++ b/src/main/java/in/koreatech/koin/domain/timetableV2/service/TimetableLectureService.java @@ -0,0 +1,83 @@ +package in.koreatech.koin.domain.timetableV2.service; + +import static in.koreatech.koin.domain.timetableV2.util.GradeCalculator.calculateGradesMainFrame; +import static in.koreatech.koin.domain.timetableV2.util.GradeCalculator.calculateTotalGrades; +import static in.koreatech.koin.domain.timetableV2.validation.TimetableFrameValidate.validateUserAuthorization; + +import java.util.List; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import in.koreatech.koin.domain.timetableV2.dto.request.TimetableLectureCreateRequest; +import in.koreatech.koin.domain.timetableV2.dto.request.TimetableLectureUpdateRequest; +import in.koreatech.koin.domain.timetableV2.dto.response.TimetableLectureResponse; +import in.koreatech.koin.domain.timetableV2.model.TimetableFrame; +import in.koreatech.koin.domain.timetableV2.model.TimetableLecture; +import in.koreatech.koin.domain.timetableV2.repository.TimetableFrameRepositoryV2; +import in.koreatech.koin.domain.timetableV2.repository.TimetableLectureRepositoryV2; +import in.koreatech.koin.domain.timetableV2.factory.TimetableLectureCreator; +import in.koreatech.koin.domain.timetableV2.factory.TimetableLectureUpdater; +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class TimetableLectureService { + + private final TimetableLectureRepositoryV2 timetableLectureRepositoryV2; + private final TimetableFrameRepositoryV2 timetableFrameRepositoryV2; + private final TimetableLectureCreator timetableLectureCreator; + private final TimetableLectureUpdater timetableLectureUpdater; + + @Transactional + public TimetableLectureResponse createTimetableLectures(Integer userId, TimetableLectureCreateRequest request) { + TimetableFrame frame = timetableFrameRepositoryV2.getById(request.timetableFrameId()); + validateUserAuthorization(frame.getUser().getId(), userId); + timetableLectureCreator.createTimetableLectures(request, frame); + return getTimetableLectureResponse(userId, frame); + } + + @Transactional + public TimetableLectureResponse updateTimetablesLectures(Integer userId, TimetableLectureUpdateRequest request) { + TimetableFrame frame = timetableFrameRepositoryV2.getById(request.timetableFrameId()); + validateUserAuthorization(frame.getUser().getId(), userId); + timetableLectureUpdater.updateTimetablesLectures(request); + return getTimetableLectureResponse(userId, frame); + } + + public TimetableLectureResponse getTimetableLectures(Integer userId, Integer timetableFrameId) { + TimetableFrame timetableFrame = timetableFrameRepositoryV2.getById(timetableFrameId); + validateUserAuthorization(timetableFrame.getUser().getId(), userId); + return getTimetableLectureResponse(userId, timetableFrame); + } + + @Transactional + public void deleteTimetableLecture(Integer userId, Integer timetableLectureId) { + TimetableLecture timetableLecture = timetableLectureRepositoryV2.getById(timetableLectureId); + TimetableFrame timetableFrame = timetableLecture.getTimetableFrame(); + validateUserAuthorization(timetableFrame.getUser().getId(), userId); + timetableLectureRepositoryV2.deleteById(timetableLectureId); + } + + private TimetableLectureResponse getTimetableLectureResponse(Integer userId, TimetableFrame timetableFrame) { + int grades = calculateGradesMainFrame(timetableFrame); + int totalGrades = calculateTotalGrades(timetableFrameRepositoryV2.findByUserIdAndIsMainTrue(userId)); + return TimetableLectureResponse.of(timetableFrame, grades, totalGrades); + } + + @Transactional + public void deleteTimetableLectures(List request, Integer userId) { + request.stream() + .map(timetableLectureRepositoryV2::getById) + .peek(lecture -> validateUserAuthorization(lecture.getTimetableFrame().getId(), userId)) + .forEach(lecture -> timetableLectureRepositoryV2.deleteById(lecture.getId())); + } + + @Transactional + public void deleteTimetableLectureByFrameId(Integer frameId, Integer lectureId, Integer userId) { + TimetableFrame frame = timetableFrameRepositoryV2.getById(frameId); + validateUserAuthorization(frame.getUser().getId(), userId); + timetableLectureRepositoryV2.deleteByFrameIdAndLectureId(frameId, lectureId); + } +} diff --git a/src/main/java/in/koreatech/koin/domain/timetableV2/service/TimetableServiceV2.java b/src/main/java/in/koreatech/koin/domain/timetableV2/service/TimetableServiceV2.java deleted file mode 100644 index ba99240b3..000000000 --- a/src/main/java/in/koreatech/koin/domain/timetableV2/service/TimetableServiceV2.java +++ /dev/null @@ -1,230 +0,0 @@ -package in.koreatech.koin.domain.timetableV2.service; - -import static in.koreatech.koin.domain.timetableV2.dto.TimetableLectureCreateRequest.InnerTimeTableLectureRequest; -import static in.koreatech.koin.domain.timetableV2.dto.TimetableLectureUpdateRequest.InnerTimetableLectureRequest; - -import java.util.List; -import java.util.Objects; - -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import in.koreatech.koin.domain.timetable.exception.SemesterNotFoundException; -import in.koreatech.koin.domain.timetable.model.Lecture; -import in.koreatech.koin.domain.timetable.model.Semester; -import in.koreatech.koin.domain.timetableV2.dto.TimetableFrameCreateRequest; -import in.koreatech.koin.domain.timetableV2.dto.TimetableFrameResponse; -import in.koreatech.koin.domain.timetableV2.dto.TimetableFrameUpdateRequest; -import in.koreatech.koin.domain.timetableV2.dto.TimetableFrameUpdateResponse; -import in.koreatech.koin.domain.timetableV2.dto.TimetableLectureCreateRequest; -import in.koreatech.koin.domain.timetableV2.dto.TimetableLectureResponse; -import in.koreatech.koin.domain.timetableV2.dto.TimetableLectureUpdateRequest; -import in.koreatech.koin.domain.timetableV2.model.TimetableFrame; -import in.koreatech.koin.domain.timetableV2.model.TimetableLecture; -import in.koreatech.koin.domain.timetableV2.repository.LectureRepositoryV2; -import in.koreatech.koin.domain.timetableV2.repository.SemesterRepositoryV2; -import in.koreatech.koin.domain.timetableV2.repository.TimetableFrameRepositoryV2; -import in.koreatech.koin.domain.timetableV2.repository.TimetableLectureRepositoryV2; -import in.koreatech.koin.domain.user.model.User; -import in.koreatech.koin.domain.user.repository.UserRepository; -import in.koreatech.koin.global.auth.exception.AuthorizationException; -import in.koreatech.koin.global.concurrent.ConcurrencyGuard; -import in.koreatech.koin.global.exception.KoinIllegalArgumentException; -import lombok.RequiredArgsConstructor; - -@Service -@RequiredArgsConstructor -@Transactional(readOnly = true) -public class TimetableServiceV2 { - - private final LectureRepositoryV2 lectureRepositoryV2; - private final TimetableLectureRepositoryV2 timetableLectureRepositoryV2; - private final TimetableFrameRepositoryV2 timetableFrameRepositoryV2; - private final UserRepository userRepository; - private final SemesterRepositoryV2 semesterRepositoryV2; - - @Transactional - public TimetableFrameResponse createTimetablesFrame(Integer userId, TimetableFrameCreateRequest request) { - Semester semester = semesterRepositoryV2.getBySemester(request.semester()); - User user = userRepository.getById(userId); - int currentFrameCount = timetableFrameRepositoryV2.countByUserIdAndSemesterId(userId, semester.getId()); - boolean isMain = (currentFrameCount == 0); - String name = (request.timetableName() != null) ? request.timetableName() : "시간표" + (currentFrameCount + 1); - TimetableFrame timetableFrame = request.toTimetablesFrame(user, semester, name, isMain); - TimetableFrame savedTimetableFrame = timetableFrameRepositoryV2.save(timetableFrame); - return TimetableFrameResponse.from(savedTimetableFrame); - } - - @Transactional - public TimetableFrameUpdateResponse updateTimetableFrame(Integer timetableFrameId, - TimetableFrameUpdateRequest timetableFrameUpdateRequest, Integer userId) { - TimetableFrame timeTableFrame = timetableFrameRepositoryV2.getById(timetableFrameId); - Semester semester = timeTableFrame.getSemester(); - boolean isMain = timetableFrameUpdateRequest.isMain(); - if (isMain) { - cancelMainTimetable(userId, semester.getId()); - } else { - if (timeTableFrame.isMain()) { - throw new KoinIllegalArgumentException("메인 시간표는 필수입니다."); - } - } - timeTableFrame.updateTimetableFrame(semester, timetableFrameUpdateRequest.timetableName(), isMain); - return TimetableFrameUpdateResponse.from(timeTableFrame); - } - - public List getTimetablesFrame(Integer userId, String semesterRequest) { - Semester semester = semesterRepositoryV2.getBySemester(semesterRequest); - return timetableFrameRepositoryV2.findAllByUserIdAndSemesterId(userId, semester.getId()).stream() - .map(TimetableFrameResponse::from) - .toList(); - } - - @Transactional - public void deleteTimetablesFrame(Integer userId, Integer frameId) { - TimetableFrame frame = timetableFrameRepositoryV2.getByIdWithLock(frameId); - if (!Objects.equals(frame.getUser().getId(), userId)) { - throw AuthorizationException.withDetail("userId: " + userId); - } - - deleteFrameAndUpdateMainStatusWithLock(frameId, frame); - } - - @ConcurrencyGuard(lockName = "deleteFrame") - private void deleteFrameAndUpdateMainStatusWithLock(Integer frameId, TimetableFrame frame) { - timetableFrameRepositoryV2.deleteById(frameId); - - if (frame.isMain()) { - TimetableFrame nextMainFrame = - timetableFrameRepositoryV2.findFirstByUserIdAndSemesterIdAndIsMainFalseOrderByCreatedAtAsc( - frame.getUser().getId(), - frame.getSemester().getId() - ); - if (nextMainFrame != null) { - nextMainFrame.updateStatusMain(true); - } - } - } - - @Transactional - public TimetableLectureResponse createTimetableLectures(Integer userId, TimetableLectureCreateRequest request) { - TimetableFrame timetableFrame = timetableFrameRepositoryV2.getById(request.timetableFrameId()); - if (!Objects.equals(timetableFrame.getUser().getId(), userId)) { - throw AuthorizationException.withDetail("userId: " + userId); - } - - for (InnerTimeTableLectureRequest timetableLectureRequest : request.timetableLecture()) { - Lecture lecture = timetableLectureRequest.lectureId() == null ? - null : lectureRepositoryV2.getLectureById(timetableLectureRequest.lectureId()); - TimetableLecture timetableLecture = timetableLectureRequest.toTimetableLecture(timetableFrame, lecture); - timetableLectureRepositoryV2.save(timetableLecture); - } - - List timetableLectures = timetableLectureRepositoryV2.findAllByTimetableFrameId( - timetableFrame.getId()); - return getTimetableLectureResponse(userId, timetableFrame, timetableLectures); - } - - @Transactional - public TimetableLectureResponse updateTimetablesLectures(Integer userId, TimetableLectureUpdateRequest request) { - TimetableFrame timetableFrame = timetableFrameRepositoryV2.getById(request.timetableFrameId()); - if (!Objects.equals(timetableFrame.getUser().getId(), userId)) { - throw AuthorizationException.withDetail("userId: " + userId); - } - - for (InnerTimetableLectureRequest timetableRequest : request.timetableLecture()) { - TimetableLecture timetableLecture = timetableLectureRepositoryV2.getById(timetableRequest.id()); - timetableLecture.update( - timetableRequest.classTitle(), - timetableRequest.classTime().toString(), - timetableRequest.classPlace(), - timetableRequest.professor(), - timetableRequest.grades(), - timetableRequest.memo()); - } - List timetableLectures = timetableFrame.getTimetableLectures(); - return getTimetableLectureResponse(userId, timetableFrame, timetableLectures); - } - - @Transactional - public TimetableLectureResponse getTimetableLectures(Integer userId, Integer timetableFrameId) { - TimetableFrame frame = timetableFrameRepositoryV2.getById(timetableFrameId); - List timetableLectures = frame.getTimetableLectures(); - if (!Objects.equals(frame.getUser().getId(), userId)) { - throw AuthorizationException.withDetail("userId: " + userId); - } - return getTimetableLectureResponse(userId, frame, timetableLectures); - } - - @Transactional - public void deleteTimetableLecture(Integer userId, Integer timetableLectureId) { - TimetableLecture timetableLecture = timetableLectureRepositoryV2.getById(timetableLectureId); - TimetableFrame frame = timetableLecture.getTimetableFrame(); - if (!Objects.equals(frame.getUser().getId(), userId)) { - throw AuthorizationException.withDetail("userId: " + userId); - } - timetableLectureRepositoryV2.deleteById(timetableLectureId); - } - - private TimetableLectureResponse getTimetableLectureResponse(Integer userId, TimetableFrame timetableFrame, - List timetableLectures) { - int grades = 0; - int totalGrades = 0; - - if (timetableFrame.isMain()) { - grades = calculateGrades(timetableLectures); - } - - for (TimetableFrame timetableFrames : timetableFrameRepositoryV2.findByUserIdAndIsMainTrue(userId)) { - totalGrades += calculateGrades( - timetableLectureRepositoryV2.findAllByTimetableFrameId(timetableFrames.getId())); - } - - return TimetableLectureResponse.of(timetableFrame.getId(), timetableLectures, grades, totalGrades); - } - - private int calculateGrades(List timetableLectures) { - return timetableLectures.stream() - .mapToInt(lecture -> { - if (lecture.getLecture() != null) { - return Integer.parseInt(lecture.getLecture().getGrades()); - } else { - return Integer.parseInt(lecture.getGrades()); - } - }) - .sum(); - } - - private void cancelMainTimetable(Integer userId, Integer semesterId) { - TimetableFrame mainTimetableFrame = timetableFrameRepositoryV2.getMainTimetableByUserIdAndSemesterId(userId, - semesterId); - mainTimetableFrame.cancelMain(); - } - - @Transactional - public void deleteAllTimetablesFrame(Integer userId, String semester) { - User user = userRepository.findById(userId).get(); - Semester userSemester = semesterRepositoryV2.findBySemester(semester) - .orElseThrow(() -> new SemesterNotFoundException("해당하는 시간표 프레임이 없습니다")); - timetableFrameRepositoryV2.deleteAllByUserAndSemester(user, userSemester); - } - - @Transactional - public void deleteTimetableLectures(List request, Integer userId) { - for (int timetablesLectureId : request) { - TimetableLecture timetableLecture = timetableLectureRepositoryV2.getById(timetablesLectureId); - if (!Objects.equals(timetableLecture.getTimetableFrame().getUser().getId(), userId)) { - throw AuthorizationException.withDetail("userId: " + userId); - } - timetableLectureRepositoryV2.deleteById(timetablesLectureId); - } - } - - @Transactional - public void deleteTimetableLectureByFrameId(Integer frameId, Integer lectureId, Integer userId) { - TimetableFrame timetableFrame = timetableFrameRepositoryV2.getById(frameId); - if (!Objects.equals(timetableFrame.getUser().getId(), userId)) { - throw AuthorizationException.withDetail("userId: " + userId); - } - timetableLectureRepositoryV2.deleteByFrameIdAndLectureId(frameId, lectureId); - } -} diff --git a/src/main/java/in/koreatech/koin/domain/timetableV2/util/GradeCalculator.java b/src/main/java/in/koreatech/koin/domain/timetableV2/util/GradeCalculator.java new file mode 100644 index 000000000..e887fd538 --- /dev/null +++ b/src/main/java/in/koreatech/koin/domain/timetableV2/util/GradeCalculator.java @@ -0,0 +1,39 @@ +package in.koreatech.koin.domain.timetableV2.util; + +import static lombok.AccessLevel.PRIVATE; + +import java.util.List; + +import in.koreatech.koin.domain.timetableV2.model.TimetableFrame; +import in.koreatech.koin.domain.timetableV2.model.TimetableLecture; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = PRIVATE) +public class GradeCalculator { + + public static int calculateGradesMainFrame(TimetableFrame timetableFrame) { + if (timetableFrame.isMain()) { + return calculateGrades(timetableFrame.getTimetableLectures()); + } + return 0; + } + + public static int calculateTotalGrades(List timetableFrames) { + return timetableFrames.stream() + .mapToInt(timetableFrame -> calculateGrades(timetableFrame.getTimetableLectures())) + .sum(); + } + + private static int calculateGrades(List timetableLectures) { + return timetableLectures.stream() + .mapToInt(GradeCalculator::determineGrade) + .sum(); + } + + private static int determineGrade(TimetableLecture timetableLecture) { + if (timetableLecture.getLecture() != null) { + return Integer.parseInt(timetableLecture.getLecture().getGrades()); + } + return Integer.parseInt(timetableLecture.getGrades()); + } +} diff --git a/src/main/java/in/koreatech/koin/domain/timetableV2/validation/TimetableFrameValidate.java b/src/main/java/in/koreatech/koin/domain/timetableV2/validation/TimetableFrameValidate.java new file mode 100644 index 000000000..2dd77b3d7 --- /dev/null +++ b/src/main/java/in/koreatech/koin/domain/timetableV2/validation/TimetableFrameValidate.java @@ -0,0 +1,26 @@ +package in.koreatech.koin.domain.timetableV2.validation; + +import static lombok.AccessLevel.PRIVATE; + +import java.util.Objects; + +import in.koreatech.koin.domain.timetableV2.model.TimetableFrame; +import in.koreatech.koin.global.auth.exception.AuthorizationException; +import in.koreatech.koin.global.exception.KoinIllegalArgumentException; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = PRIVATE) +public class TimetableFrameValidate { + + public static void validateUserAuthorization(Integer frameUserId, Integer userId) { + if (!Objects.equals(frameUserId, userId)) { + throw AuthorizationException.withDetail("userId: " + userId); + } + } + + public static void validateTimetableFrameUpdate(TimetableFrame timeTableFrame, boolean isMain) { + if (timeTableFrame.isMain() && !isMain) { + throw new KoinIllegalArgumentException("메인 시간표는 필수입니다."); + } + } +} diff --git a/src/test/java/in/koreatech/koin/acceptance/TimetableFrameApiTest.java b/src/test/java/in/koreatech/koin/acceptance/TimetableFrameApiTest.java new file mode 100644 index 000000000..27cd4e79e --- /dev/null +++ b/src/test/java/in/koreatech/koin/acceptance/TimetableFrameApiTest.java @@ -0,0 +1,237 @@ +package in.koreatech.koin.acceptance; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.TestInstance.Lifecycle; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.transaction.annotation.Transactional; + +import in.koreatech.koin.AcceptanceTest; +import in.koreatech.koin.domain.timetable.model.Lecture; +import in.koreatech.koin.domain.timetable.model.Semester; +import in.koreatech.koin.domain.timetableV2.model.TimetableFrame; +import in.koreatech.koin.domain.timetableV2.repository.TimetableFrameRepositoryV2; +import in.koreatech.koin.domain.timetableV2.repository.TimetableLectureRepositoryV2; +import in.koreatech.koin.domain.user.model.User; +import in.koreatech.koin.fixture.LectureFixture; +import in.koreatech.koin.fixture.SemesterFixture; +import in.koreatech.koin.fixture.TimeTableV2Fixture; +import in.koreatech.koin.fixture.UserFixture; + +@SuppressWarnings("NonAsciiCharacters") +@Transactional +@TestInstance(Lifecycle.PER_CLASS) +public class TimetableFrameApiTest extends AcceptanceTest { + + @Autowired + private TimeTableV2Fixture timetableV2Fixture; + + @Autowired + private UserFixture userFixture; + + @Autowired + private SemesterFixture semesterFixture; + + @Autowired + private LectureFixture lectureFixture; + + @Autowired + private TimetableFrameRepositoryV2 timetableFrameRepositoryV2; + + @Autowired + private TimetableLectureRepositoryV2 timetableLectureRepositoryV2; + + private User user; + private String token; + private Semester semester; + + @BeforeAll + void setup() { + clear(); + user = userFixture.준호_학생().getUser(); + token = userFixture.getToken(user); + semester = semesterFixture.semester("20192"); + } + + @Test + void 특정_시간표_frame을_생성한다() throws Exception { + mockMvc.perform( + post("/v2/timetables/frame") + .header("Authorization", "Bearer " + token) + .content(String.format(""" + { + "semester": "%s" + } + """, semester.getSemester() + )) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" + { + "id": 1, + "timetable_name": "시간표1", + "is_main": true + } + """)); + } + + @Test + void 특정_시간표_frame을_이름을_지어_생성한다() throws Exception { + mockMvc.perform( + post("/v2/timetables/frame") + .header("Authorization", "Bearer " + token) + .content(String.format(""" + { + "semester": "%s", + "timetable_name": "%s" + } + """, semester.getSemester(), "이름지어본시간표" + )) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" + { + "id": 1, + "timetable_name": "이름지어본시간표", + "is_main": true + } + """)); + } + + @Test + void 특정_시간표_frame을_수정한다() throws Exception { + TimetableFrame frame = timetableV2Fixture.시간표1(user, semester); + Integer frameId = frame.getId(); + + mockMvc.perform( + put("/v2/timetables/frame/{id}", frameId) + .header("Authorization", "Bearer " + token) + .content(""" + { + "timetable_name": "새로운 이름", + "is_main": true + } + """ + ) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" + { + "id": 1, + "timetable_name": "새로운 이름", + "is_main": true + } + """)); + } + + @Test + void 모든_시간표_frame을_조회한다() throws Exception { + timetableV2Fixture.시간표1(user, semester); + timetableV2Fixture.시간표2(user, semester); + + mockMvc.perform( + get("/v2/timetables/frames") + .header("Authorization", "Bearer " + token) + .param("semester", semester.getSemester()) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" + [ + { + "id": 1, + "timetable_name": "시간표1", + "is_main": true + }, + { + "id": 2, + "timetable_name": "시간표2", + "is_main": false + } + ] + """)); + } + + @Test + void 강의를_담고_있는_특정_시간표_frame을_삭제한다() throws Exception { + Lecture lecture = lectureFixture.HRD_개론(semester.getSemester()); + + TimetableFrame frame1 = timetableV2Fixture.시간표5(user, semester, lecture); + + mockMvc.perform( + delete("/v2/timetables/frame") + .header("Authorization", "Bearer " + token) + .param("id", String.valueOf(frame1.getId())) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isNoContent()); + + assertThat(timetableFrameRepositoryV2.findById(frame1.getId())).isNotPresent(); + assertThat(timetableLectureRepositoryV2.findById(frame1.getTimetableLectures().get(1).getId())).isNotPresent(); + } + + @Test + void isMain인_frame을_삭제한다_다른_frame이_main으로_됨() throws Exception { + TimetableFrame frame1 = timetableV2Fixture.시간표1(user, semester); + TimetableFrame frame2 = timetableV2Fixture.시간표2(user, semester); + + mockMvc.perform( + delete("/v2/timetables/frame") + .header("Authorization", "Bearer " + token) + .param("id", String.valueOf(frame1.getId())) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isNoContent()); + + assertThat(timetableFrameRepositoryV2.findById(frame1.getId())).isNotPresent(); + + TimetableFrame reloadedFrame2 = timetableFrameRepositoryV2.findById(frame2.getId()).orElseThrow(); + assertThat(reloadedFrame2.isMain()).isTrue(); + } + + @Test + void 특정_시간표_frame을_삭제한다_본인_삭제가_아니면_403_반환() throws Exception { + User user1 = userFixture.성빈_학생().getUser(); + String token = userFixture.getToken(user1); + + TimetableFrame frame1 = timetableV2Fixture.시간표1(user, semester); + + mockMvc.perform( + delete("/v2/timetables/frame") + .header("Authorization", "Bearer " + token) + .param("id", String.valueOf(frame1.getId())) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isForbidden()); + } + + @Test + void 모든_시간표_프레임을_삭제한다() throws Exception { + TimetableFrame frame1 = timetableV2Fixture.시간표1(user, semester); + TimetableFrame frame2 = timetableV2Fixture.시간표1(user, semester); + TimetableFrame frame3 = timetableV2Fixture.시간표1(user, semester); + + mockMvc.perform( + delete("/v2/all/timetables/frame") + .header("Authorization", "Bearer " + token) + .param("semester", semester.getSemester()) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isNoContent()); + + assertThat(timetableFrameRepositoryV2.findById(frame1.getId())).isNotPresent(); + assertThat(timetableFrameRepositoryV2.findById(frame2.getId())).isNotPresent(); + assertThat(timetableFrameRepositoryV2.findById(frame3.getId())).isNotPresent(); + } +} diff --git a/src/test/java/in/koreatech/koin/acceptance/TimetableV2ApiTest.java b/src/test/java/in/koreatech/koin/acceptance/TimetableLectureApiTest.java similarity index 60% rename from src/test/java/in/koreatech/koin/acceptance/TimetableV2ApiTest.java rename to src/test/java/in/koreatech/koin/acceptance/TimetableLectureApiTest.java index 2b041c2c7..a514d6794 100644 --- a/src/test/java/in/koreatech/koin/acceptance/TimetableV2ApiTest.java +++ b/src/test/java/in/koreatech/koin/acceptance/TimetableLectureApiTest.java @@ -1,6 +1,5 @@ package in.koreatech.koin.acceptance; -import static org.assertj.core.api.Assertions.assertThat; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -20,8 +19,6 @@ import in.koreatech.koin.domain.timetable.model.Semester; import in.koreatech.koin.domain.timetableV2.model.TimetableFrame; import in.koreatech.koin.domain.timetableV2.model.TimetableLecture; -import in.koreatech.koin.domain.timetableV2.repository.TimetableFrameRepositoryV2; -import in.koreatech.koin.domain.timetableV2.repository.TimetableLectureRepositoryV2; import in.koreatech.koin.domain.user.model.User; import in.koreatech.koin.fixture.LectureFixture; import in.koreatech.koin.fixture.SemesterFixture; @@ -31,7 +28,7 @@ @SuppressWarnings("NonAsciiCharacters") @Transactional @TestInstance(TestInstance.Lifecycle.PER_CLASS) -public class TimetableV2ApiTest extends AcceptanceTest { +public class TimetableLectureApiTest extends AcceptanceTest { @Autowired private TimeTableV2Fixture timetableV2Fixture; @@ -45,224 +42,20 @@ public class TimetableV2ApiTest extends AcceptanceTest { @Autowired private LectureFixture lectureFixture; - @Autowired - private TimetableFrameRepositoryV2 timetableFrameRepositoryV2; - - @Autowired - private TimetableLectureRepositoryV2 timetableLectureRepositoryV2; + private User user; + private String token; + private Semester semester; @BeforeAll void setup() { clear(); - } - - @Test - void 특정_시간표_frame을_생성한다() throws Exception { - User user = userFixture.준호_학생().getUser(); - String token = userFixture.getToken(user); - Semester semester = semesterFixture.semester("20192"); - - mockMvc.perform( - post("/v2/timetables/frame") - .header("Authorization", "Bearer " + token) - .content(String.format(""" - { - "semester": "%s" - } - """, semester.getSemester() - )) - .contentType(MediaType.APPLICATION_JSON) - ) - .andExpect(status().isOk()) - .andExpect(content().json(""" - { - "id": 1, - "timetable_name": "시간표1", - "is_main": true - } - """)); - } - - @Test - void 특정_시간표_frame을_이름을_지어_생성한다() throws Exception { - User user = userFixture.준호_학생().getUser(); - String token = userFixture.getToken(user); - Semester semester = semesterFixture.semester("20192"); - - mockMvc.perform( - post("/v2/timetables/frame") - .header("Authorization", "Bearer " + token) - .content(String.format(""" - { - "semester": "%s", - "timetable_name": "%s" - } - """, semester.getSemester(), "이름지어본시간표" - )) - .contentType(MediaType.APPLICATION_JSON) - ) - .andExpect(status().isOk()) - .andExpect(content().json(""" - { - "id": 1, - "timetable_name": "이름지어본시간표", - "is_main": true - } - """)); - } - - @Test - void 특정_시간표_frame을_수정한다() throws Exception { - User user = userFixture.준호_학생().getUser(); - String token = userFixture.getToken(user); - Semester semester = semesterFixture.semester("20192"); - TimetableFrame frame = timetableV2Fixture.시간표1(user, semester); - Integer frameId = frame.getId(); - - mockMvc.perform( - put("/v2/timetables/frame/{id}", frameId) - .header("Authorization", "Bearer " + token) - .content(String.format(""" - { - "timetable_name": "새로운 이름", - "is_main": true - } - """ - )) - .contentType(MediaType.APPLICATION_JSON) - ) - .andExpect(status().isOk()) - .andExpect(content().json(""" - { - "id": 1, - "timetable_name": "새로운 이름", - "is_main": true - } - """)); - } - - @Test - void 모든_시간표_frame을_조회한다() throws Exception { - User user = userFixture.준호_학생().getUser(); - String token = userFixture.getToken(user); - Semester semester = semesterFixture.semester("20192"); - - timetableV2Fixture.시간표1(user, semester); - timetableV2Fixture.시간표2(user, semester); - - mockMvc.perform( - get("/v2/timetables/frames") - .header("Authorization", "Bearer " + token) - .param("semester", semester.getSemester()) - .contentType(MediaType.APPLICATION_JSON) - ) - .andExpect(status().isOk()) - .andExpect(content().json(""" - [ - { - "id": 1, - "timetable_name": "시간표1", - "is_main": true - }, - { - "id": 2, - "timetable_name": "시간표2", - "is_main": false - } - ] - """)); - } - - @Test - void 강의를_담고_있는_특정_시간표_frame을_삭제한다() throws Exception { - User user = userFixture.준호_학생().getUser(); - String token = userFixture.getToken(user); - Semester semester = semesterFixture.semester("20192"); - Lecture lecture = lectureFixture.HRD_개론(semester.getSemester()); - - TimetableFrame frame1 = timetableV2Fixture.시간표5(user, semester, lecture); - - mockMvc.perform( - delete("/v2/timetables/frame") - .header("Authorization", "Bearer " + token) - .param("id", String.valueOf(frame1.getId())) - .contentType(MediaType.APPLICATION_JSON) - ) - .andExpect(status().isNoContent()); - - assertThat(timetableFrameRepositoryV2.findById(frame1.getId())).isNotPresent(); - assertThat(timetableLectureRepositoryV2.findById(frame1.getTimetableLectures().get(1).getId())).isNotPresent(); - } - - @Test - void isMain인_frame을_삭제한다_다른_frame이_main으로_됨() throws Exception { - User user = userFixture.준호_학생().getUser(); - String token = userFixture.getToken(user); - Semester semester = semesterFixture.semester("20192"); - - TimetableFrame frame1 = timetableV2Fixture.시간표1(user, semester); - TimetableFrame frame2 = timetableV2Fixture.시간표2(user, semester); - - mockMvc.perform( - delete("/v2/timetables/frame") - .header("Authorization", "Bearer " + token) - .param("id", String.valueOf(frame1.getId())) - .contentType(MediaType.APPLICATION_JSON) - ) - .andExpect(status().isNoContent()); - - assertThat(timetableFrameRepositoryV2.findById(frame1.getId())).isNotPresent(); - - TimetableFrame reloadedFrame2 = timetableFrameRepositoryV2.findById(frame2.getId()).orElseThrow(); - assertThat(reloadedFrame2.isMain()).isTrue(); - } - - @Test - void 특정_시간표_frame을_삭제한다_본인_삭제가_아니면_403_반환() throws Exception { - User user1 = userFixture.준호_학생().getUser(); - User user2 = userFixture.성빈_학생().getUser(); - String token = userFixture.getToken(user2); - Semester semester = semesterFixture.semester("20192"); - - TimetableFrame frame1 = timetableV2Fixture.시간표1(user1, semester); - - mockMvc.perform( - delete("/v2/timetables/frame") - .header("Authorization", "Bearer " + token) - .param("id", String.valueOf(frame1.getId())) - .contentType(MediaType.APPLICATION_JSON) - ) - .andExpect(status().isForbidden()); - } - - @Test - void 모든_시간표_프레임을_삭제한다() throws Exception { - User user = userFixture.준호_학생().getUser(); - String token = userFixture.getToken(user); - Semester semester = semesterFixture.semester("20192"); - - TimetableFrame frame1 = timetableV2Fixture.시간표1(user, semester); - TimetableFrame frame2 = timetableV2Fixture.시간표1(user, semester); - TimetableFrame frame3 = timetableV2Fixture.시간표1(user, semester); - - mockMvc.perform( - delete("/v2/all/timetables/frame") - .header("Authorization", "Bearer " + token) - .param("semester", semester.getSemester()) - .contentType(MediaType.APPLICATION_JSON) - ) - .andExpect(status().isNoContent()); - - assertThat(timetableFrameRepositoryV2.findById(frame1.getId())).isNotPresent(); - assertThat(timetableFrameRepositoryV2.findById(frame2.getId())).isNotPresent(); - assertThat(timetableFrameRepositoryV2.findById(frame3.getId())).isNotPresent(); + user = userFixture.준호_학생().getUser(); + token = userFixture.getToken(user); + semester = semesterFixture.semester("20192"); } @Test void 시간표를_생성한다_TimetableLecture() throws Exception { - User user = userFixture.준호_학생().getUser(); - String token = userFixture.getToken(user); - Semester semester = semesterFixture.semester("20192"); timetableV2Fixture.시간표1(user, semester); mockMvc.perform( @@ -339,11 +132,7 @@ void setup() { @Test void 시간표를_수정한다_TimetableLecture() throws Exception { - User user = userFixture.준호_학생().getUser(); - String token = userFixture.getToken(user); - Semester semester = semesterFixture.semester("20192"); - TimetableFrame frame = timetableV2Fixture.시간표3(user, semester); - Integer frameId = frame.getId(); + timetableV2Fixture.시간표3(user, semester); mockMvc.perform( put("/v2/timetables/lecture") @@ -421,10 +210,6 @@ void setup() { @Test void 시간표를_조회한다_TimetableLecture() throws Exception { - User user = userFixture.준호_학생().getUser(); - String token = userFixture.getToken(user); - Semester semester = semesterFixture.semester("20192"); - Lecture 건축구조의_이해_및_실습 = lectureFixture.건축구조의_이해_및_실습(semester.getSemester()); Lecture HRD_개론 = lectureFixture.HRD_개론(semester.getSemester()); @@ -482,12 +267,9 @@ void setup() { @Test void 시간표에서_특정_강의를_삭제한다() throws Exception { - User user1 = userFixture.준호_학생().getUser(); - String token = userFixture.getToken(user1); - Semester semester = semesterFixture.semester("20192"); Lecture lecture1 = lectureFixture.HRD_개론("20192"); Lecture lecture2 = lectureFixture.영어청해("20192"); - TimetableFrame frame = timetableV2Fixture.시간표4(user1, semester, lecture1, lecture2); + TimetableFrame frame = timetableV2Fixture.시간표4(user, semester, lecture1, lecture2); Integer lectureId = lecture1.getId(); @@ -502,12 +284,9 @@ void setup() { @Test void 시간표에서_특정_강의를_삭제한다_V2() throws Exception { - User user1 = userFixture.준호_학생().getUser(); - String token = userFixture.getToken(user1); - Semester semester = semesterFixture.semester("20192"); Lecture lecture1 = lectureFixture.HRD_개론("20192"); Lecture lecture2 = lectureFixture.영어청해("20192"); - TimetableFrame frame = timetableV2Fixture.시간표4(user1, semester, lecture1, lecture2); + TimetableFrame frame = timetableV2Fixture.시간표4(user, semester, lecture1, lecture2); Integer frameId = frame.getId(); Integer lectureId = lecture1.getId(); @@ -522,12 +301,9 @@ void setup() { @Test void 시간표에서_여러개의_강의를_한번에_삭제한다_V2() throws Exception { - User user1 = userFixture.준호_학생().getUser(); - String token = userFixture.getToken(user1); - Semester semester = semesterFixture.semester("20192"); Lecture lecture1 = lectureFixture.HRD_개론("20192"); Lecture lecture2 = lectureFixture.영어청해("20192"); - TimetableFrame frame = timetableV2Fixture.시간표4(user1, semester, lecture1, lecture2); + TimetableFrame frame = timetableV2Fixture.시간표4(user, semester, lecture1, lecture2); List timetableLectureIds = frame.getTimetableLectures().stream() .map(TimetableLecture::getId)