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 365fe50ce..5592ce41e 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 @@ -76,9 +76,9 @@ ResponseEntity updateTimetableFrame( ) @Operation(summary = "시간표 프레임 조회") @SecurityRequirement(name = "Jwt Authentication") - @GetMapping("/v2/timetables/frame") - ResponseEntity> getTimetablesFrame( - @RequestParam(name = "semester") String semester, + @GetMapping("/v2/timetables/frames") + ResponseEntity getTimetablesFrame( + @RequestParam(name = "semester", required = false) String semester, @Auth(permit = {STUDENT}) Integer userId ); 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 9546a84ff..a6f280b9b 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 @@ -55,11 +55,11 @@ public ResponseEntity updateTimetableFrame( } @GetMapping("/v2/timetables/frames") - public ResponseEntity> getTimetablesFrame( - @RequestParam(name = "semester") String semester, + public ResponseEntity getTimetablesFrame( + @RequestParam(name = "semester", required = false) String semester, @Auth(permit = {STUDENT}) Integer userId ) { - List response = frameServiceV2.getTimetablesFrame(userId, semester); + Object response = frameServiceV2.getTimetablesFrame(userId, semester); return ResponseEntity.ok(response); } diff --git a/src/main/java/in/koreatech/koin/domain/timetableV2/dto/response/TimetableFramesResponse.java b/src/main/java/in/koreatech/koin/domain/timetableV2/dto/response/TimetableFramesResponse.java new file mode 100644 index 000000000..4e382e971 --- /dev/null +++ b/src/main/java/in/koreatech/koin/domain/timetableV2/dto/response/TimetableFramesResponse.java @@ -0,0 +1,42 @@ +package in.koreatech.koin.domain.timetableV2.dto.response; + +import static com.fasterxml.jackson.databind.PropertyNamingStrategies.*; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import com.fasterxml.jackson.databind.annotation.JsonNaming; + +import in.koreatech.koin.domain.timetableV2.model.TimetableFrame; +import io.swagger.v3.oas.annotations.media.Schema; + +@JsonNaming(SnakeCaseStrategy.class) +public record TimetableFramesResponse( + @Schema(description = "학기별 시간표 프레임", example = """ + { + "20241": [ + { + "id": 1, + "timetable_name": "시간표1", + "is_main": true + }, + { + "id": 2, + "timetable_name": "시간표2", + "is_main": false + } + ] + } + """, requiredMode = Schema.RequiredMode.REQUIRED) + Map> semesters +) { + public static TimetableFramesResponse from(List timetableFrames) { + Map> groupedBySemester = timetableFrames.stream() + .collect(Collectors.groupingBy( + frame -> frame.getSemester().getSemester(), + Collectors.mapping(TimetableFrameResponse::from, Collectors.toList()) + )); + return new TimetableFramesResponse(groupedBySemester); + } +} 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 47627eabd..176bd4f3e 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 @@ -79,7 +79,7 @@ SELECT COUNT(t) FROM TimetableFrame t void deleteById(Integer id); - List findAllByUserAndSemester(User user, Semester semester); + List findAllByUserAndSemester(User user, Semester semester); void deleteAllByUser(User user); @@ -90,4 +90,7 @@ default TimetableFrame getByIdWithDeleted(Integer id) { return findByIdWithDeleted(id) .orElseThrow(() -> TimetableFrameNotFoundException.withDetail("id: " + id)); } + void deleteAllByUserAndSemester(User user, Semester semester); + + List findAllByUserId(Integer userId); } 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 index 4e7a690d8..9fe8887ec 100644 --- a/src/main/java/in/koreatech/koin/domain/timetableV2/service/TimetableFrameService.java +++ b/src/main/java/in/koreatech/koin/domain/timetableV2/service/TimetableFrameService.java @@ -4,6 +4,7 @@ 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; @@ -13,6 +14,7 @@ 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.dto.response.TimetableFramesResponse; import in.koreatech.koin.domain.timetableV2.model.TimetableFrame; import in.koreatech.koin.domain.timetableV2.repository.SemesterRepositoryV2; import in.koreatech.koin.domain.timetableV2.repository.TimetableFrameRepositoryV2; @@ -20,6 +22,8 @@ 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 @@ -54,13 +58,22 @@ public TimetableFrameUpdateResponse updateTimetableFrame( return timetableFrameUpdater.updateTimetableFrame(frame, userId, request.timetableName(), request.isMain()); } - public List getTimetablesFrame(Integer userId, String semesterRequest) { + public Object getTimetablesFrame(Integer userId, String semesterRequest) { + if (semesterRequest == null) { + return getAllTimetablesFrame(userId); + } + Semester semester = semesterRepositoryV2.getBySemester(semesterRequest); return timetableFrameRepositoryV2.findAllByUserIdAndSemesterId(userId, semester.getId()).stream() .map(TimetableFrameResponse::from) .toList(); } + public TimetableFramesResponse getAllTimetablesFrame(Integer userId) { + List timetableFrames = timetableFrameRepositoryV2.findAllByUserId(userId); + return TimetableFramesResponse.from(timetableFrames); + } + @Transactional public void deleteAllTimetablesFrame(Integer userId, String semester) { User user = userRepository.getById(userId); diff --git a/src/test/java/in/koreatech/koin/acceptance/TimetableFrameApiTest.java b/src/test/java/in/koreatech/koin/acceptance/TimetableFrameApiTest.java index d2f9c0150..1449eeb96 100644 --- a/src/test/java/in/koreatech/koin/acceptance/TimetableFrameApiTest.java +++ b/src/test/java/in/koreatech/koin/acceptance/TimetableFrameApiTest.java @@ -234,4 +234,59 @@ void setup() { assertThat(frame2.isDeleted()).isTrue(); assertThat(frame3.isDeleted()).isTrue(); } + + @Test + void 모든_학기의_시간표_프레임을_조회한다() throws Exception { + Semester semester1 = semesterFixture.semester("20241"); + Semester semester2 = semesterFixture.semester("20242"); + + timetableV2Fixture.시간표1(user, semester1); + timetableV2Fixture.시간표2(user, semester1); + + timetableV2Fixture.시간표1(user, semester2); + timetableV2Fixture.시간표2(user, semester2); + timetableV2Fixture.시간표3(user, semester2); + + mockMvc.perform( + get("/v2/timetables/frames") + .header("Authorization", "Bearer " + token) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().json(""" + { + "semesters": { + "20241": [ + { + "id": 1, + "timetable_name": "시간표1", + "is_main": true + }, + { + "id": 2, + "timetable_name": "시간표2", + "is_main": false + } + ], + "20242": [ + { + "id": 3, + "timetable_name": "시간표1", + "is_main": true + }, + { + "id": 4, + "timetable_name": "시간표2", + "is_main": false + }, + { + "id": 5, + "timetable_name": "시간표3", + "is_main": false + } + ] + } + } + """)); + } }