Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: 삭제된 시간표 강의 복구 #1110

Merged
merged 15 commits into from
Dec 4, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -210,4 +210,40 @@ ResponseEntity<Void> deleteTimetableLectureByFrameId(
@PathVariable(value = "lectureId") Integer lectureId,
@Auth(permit = {STUDENT}) Integer userId
);

@ApiResponses(
value = {
@ApiResponse(responseCode = "200"),
@ApiResponse(responseCode = "401", content = @Content(schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "403", content = @Content(schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "404", content = @Content(schema = @Schema(hidden = true)))
}
)
@Operation(summary = "삭제한 시간표 강의 복구")
@SecurityRequirement(name = "Jwt Authentication")
@PostMapping("/v2/timetables/lecture/rollback")
ResponseEntity<TimetableLectureResponse> rollbackTimetableLecture(
@RequestBody List<Integer> timetableLecturesId,
@Auth(permit = {STUDENT}) Integer userId
);

@ApiResponses(
value = {
@ApiResponse(responseCode = "200"),
@ApiResponse(responseCode = "401", content = @Content(schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "403", content = @Content(schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "404", content = @Content(schema = @Schema(hidden = true)))
}
)
@Operation(summary = "삭제한 시간표 프레임 복구",
description = """
1. 삭제된 시간표 프레임: 삭제된 시간표 프레임과 그에 속한 강의 정보를 복구합니다. \n
2. 삭제되지 않은 시간표 프레임: 시간표 프레임에 속한 강의 정보를 복구합니다.
""")
@SecurityRequirement(name = "Jwt Authentication")
@PostMapping("/v2/timetables/frame/rollback")
ResponseEntity<TimetableLectureResponse> rollbackTimetableFrame(
@RequestBody @RequestParam(name = "timetable_frame_id") Integer timetableFrameId,
@Auth(permit = {STUDENT}) Integer userId
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public class TimetableControllerV2 implements TimetableApiV2 {

private final TimetableFrameService frameServiceV2;
private final TimetableLectureService lectureServiceV2;
private final TimetableLectureService timetableLectureService;

@PostMapping("/v2/timetables/frame")
public ResponseEntity<TimetableFrameResponse> createTimetablesFrame(
Expand Down Expand Up @@ -134,4 +135,22 @@ public ResponseEntity<Void> deleteTimetableLectureByFrameId(
lectureServiceV2.deleteTimetableLectureByFrameId(frameId, lectureId, userId);
return ResponseEntity.noContent().build();
}

@PostMapping("/v2/timetables/lecture/rollback")
public ResponseEntity<TimetableLectureResponse> rollbackTimetableLecture(
@RequestParam(name = "timetable_lectures_id") List<Integer> timetableLecturesId,
@Auth(permit = {STUDENT}) Integer userId
) {
TimetableLectureResponse response = timetableLectureService.rollbackTimetableLecture(timetableLecturesId, userId);
return ResponseEntity.ok(response);
}

@PostMapping("/v2/timetables/frame/rollback")
public ResponseEntity<TimetableLectureResponse> rollbackTimetableFrame(
@RequestParam(name = "timetable_frame_id") Integer timetableFrameId,
@Auth(permit = {STUDENT}) Integer userId
) {
TimetableLectureResponse response = timetableLectureService.rollbackTimetableFrame(timetableFrameId, userId);
return ResponseEntity.ok(response);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -101,4 +101,8 @@ public void addTimeTableLecture(TimetableLecture lecture) {
}
timetableLectures.add(lecture);
}

public void undelete() {
Soundbar91 marked this conversation as resolved.
Show resolved Hide resolved
this.isDeleted = false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -106,4 +106,8 @@ public void update(TimetableUpdateRequest.InnerTimetableRequest request) {
this.grades = grades;
this.memo = request.memo();
}

public void undelete() {
this.isDeleted = false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
import in.koreatech.koin.domain.timetable.exception.TimetableNotFoundException;
import in.koreatech.koin.domain.timetable.model.Semester;
import in.koreatech.koin.domain.timetableV2.exception.TimetableFrameNotFoundException;
import in.koreatech.koin.domain.timetableV2.exception.TimetableLectureNotFoundException;
import in.koreatech.koin.domain.timetableV2.model.TimetableFrame;
import in.koreatech.koin.domain.timetableV2.model.TimetableLecture;
import in.koreatech.koin.domain.user.model.User;
import jakarta.persistence.LockModeType;

Expand Down Expand Up @@ -82,4 +84,12 @@ SELECT COUNT(t) FROM TimetableFrame t
void deleteAllByUser(User user);

void deleteAllByUserAndSemester(User user, Semester semester);

@Query("SELECT t FROM TimetableFrame t WHERE t.id = :id")
Optional<TimetableFrame> findByIdWithDeleted(@Param("id") Integer id);
Soundbar91 marked this conversation as resolved.
Show resolved Hide resolved

default TimetableFrame getByIdWithDeleted(Integer id) {
return findByIdWithDeleted(id)
.orElseThrow(() -> TimetableFrameNotFoundException.withDetail("id: " + id));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,13 @@ default TimetableLecture getById(Integer id) {
AND lectures_id = :lectureId
""", nativeQuery = true)
void deleteByFrameIdAndLectureId(@Param("frameId") Integer frameId, @Param("lectureId") Integer lectureId);


Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

C

공백 한 줄👀

Suggested change

@Query("SELECT t FROM TimetableLecture t WHERE t.id = :id")
Optional<TimetableLecture> findByIdWithDeleted(@Param("id") Integer id);

default TimetableLecture getByIdWithDeleted(Integer id) {
return findByIdWithDeleted(id)
.orElseThrow(() -> TimetableLectureNotFoundException.withDetail("id: " + id));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
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;
Expand All @@ -21,7 +20,6 @@
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;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
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;
Expand All @@ -19,7 +18,6 @@
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 in.koreatech.koin.global.auth.exception.AuthorizationException;
import lombok.RequiredArgsConstructor;

@Service
Expand Down Expand Up @@ -82,4 +80,28 @@ public void deleteTimetableLectureByFrameId(Integer frameId, Integer lectureId,
validateUserAuthorization(frame.getUser().getId(), userId);
timetableLectureRepositoryV2.deleteByFrameIdAndLectureId(frameId, lectureId);
}

@Transactional
public TimetableLectureResponse rollbackTimetableLecture(List<Integer> timetableLecturesId, Integer userId) {
timetableLecturesId.stream()
.map(timetableLectureRepositoryV2::getByIdWithDeleted)
.peek(lecture -> validateUserAuthorization(lecture.getTimetableFrame().getUser().getId(), userId))
.forEach(TimetableLecture::undelete);
TimetableLecture timeTableLecture = timetableLectureRepositoryV2.getById(timetableLecturesId.get(0));
return getTimetableLectureResponse(userId, timeTableLecture.getTimetableFrame());
}

@Transactional
public TimetableLectureResponse rollbackTimetableFrame(Integer frameId, Integer userId) {
TimetableFrame timetableFrame = timetableFrameRepositoryV2.getByIdWithDeleted(frameId);
validateUserAuthorization(timetableFrame.getUser().getId(), userId);
timetableFrame.undelete();

timetableFrameRepositoryV2.getByIdWithDeleted(frameId).getTimetableLectures().stream()
Soundbar91 marked this conversation as resolved.
Show resolved Hide resolved
.map(TimetableLecture::getId)
.map(timetableLectureRepositoryV2::getByIdWithDeleted)
.forEach(TimetableLecture::undelete);

return getTimetableLectureResponse(userId, timetableFrame);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,140 @@ void setup() {
.andExpect(status().isNoContent());
}

@Test
void 시간표에서_삭제된_강의를_복구한다_V2() throws Exception {
Lecture 건축구조의_이해_및_실습 = lectureFixture.건축구조의_이해_및_실습(semester);
Lecture HRD_개론 = lectureFixture.HRD_개론(semester);
TimetableFrame frame = timetableV2Fixture.시간표6(user, semester, 건축구조의_이해_및_실습, HRD_개론);

List<Integer> timetableLecturesId = frame.getTimetableLectures().stream()
.map(TimetableLecture::getId)
.toList();

mockMvc.perform(
post("/v2/rollback/timetables/lecture")
.header("Authorization", "Bearer " + token)
.param("timetable_lectures_id", timetableLecturesId.stream()
.map(String::valueOf)
.collect(Collectors.joining(",")))
.contentType(MediaType.APPLICATION_JSON)
)
.andExpect(status().isOk())
.andExpect(content().json("""
{
"timetable_frame_id": 1,
"timetable": [
{
"id" : 1,
"lecture_id" : 1,
"regular_number": "25",
"code": "ARB244",
"design_score": "0",
"class_infos": [
{
"class_time": [200, 201, 202, 203, 204, 205, 206, 207],
"class_place": null
}
],
"memo": null,
"grades": "3",
"class_title": "건축구조의 이해 및 실습",
"lecture_class": "01",
"target": "디자 1 건축",
"professor": "황현식",
"department": "디자인ㆍ건축공학부"
},
{
"id": 2,
"lecture_id": 2,
"regular_number": "22",
"code": "BSM590",
"design_score": "0",
"class_infos": [
{
"class_time": [12, 13, 14, 15, 210, 211, 212, 213],
"class_place": null
}
],
"memo": null,
"grades": "3",
"class_title": "컴퓨팅사고",
"lecture_class": "06",
"target": "기공1",
"professor": "박한수,최준호",
"department": "기계공학부"
}
],
"grades": 6,
"total_grades": 6
}
"""));
}

@Test
void 삭제된_시간표프레임과_그에_해당하는_강의를_복구한다_V2() throws Exception {
Lecture 건축구조의_이해_및_실습 = lectureFixture.건축구조의_이해_및_실습(semester);
Lecture HRD_개론 = lectureFixture.HRD_개론(semester);
TimetableFrame frame = timetableV2Fixture.시간표7(user, semester, 건축구조의_이해_및_실습, HRD_개론);

mockMvc.perform(
post("/v2/rollback/timetables/frame")
.header("Authorization", "Bearer " + token)
.param("timetable_frame_id", String.valueOf(frame.getId()))
.contentType(MediaType.APPLICATION_JSON)
)
.andExpect(status().isOk())
.andExpect(content().json("""
{
"timetable_frame_id": 1,
"timetable": [
{
"id" : 1,
"lecture_id" : 1,
"regular_number": "25",
"code": "ARB244",
"design_score": "0",
"class_infos": [
{
"class_time": [200, 201, 202, 203, 204, 205, 206, 207],
"class_place": null
}
],
"memo": null,
"grades": "3",
"class_title": "건축구조의 이해 및 실습",
"lecture_class": "01",
"target": "디자 1 건축",
"professor": "황현식",
"department": "디자인ㆍ건축공학부"
},
{
"id": 2,
"lecture_id": 2,
"regular_number": "22",
"code": "BSM590",
"design_score": "0",
"class_infos": [
{
"class_time": [12, 13, 14, 15, 210, 211, 212, 213],
"class_place": null
}
],
"memo": null,
"grades": "3",
"class_title": "컴퓨팅사고",
"lecture_class": "06",
"target": "기공1",
"professor": "박한수,최준호",
"department": "기계공학부"
}
],
"grades": 6,
"total_grades": 6
}
"""));
}

/*@Test
@Transactional(propagation = Propagation.NOT_SUPPORTED)
void isMain이_false인_frame과_true인_frame을_동시에_삭제한다() {
Expand Down
29 changes: 29 additions & 0 deletions src/test/java/in/koreatech/koin/fixture/TimeTableV2Fixture.java
Original file line number Diff line number Diff line change
Expand Up @@ -171,4 +171,33 @@ public TimeTableV2Fixture(

return timetableFrameRepositoryV2.save(frame);
}

public TimetableFrame 시간표7(User user, Semester semester, Lecture lecture1, Lecture lecture2) {
TimetableFrame frame = TimetableFrame.builder()
.user(user)
.semester(semester)
.name("시간표7")
.isMain(true)
.timetableLectures(new ArrayList<>())
.build();

TimetableLecture timetableLecture1 = TimetableLecture.builder()
.grades("0")
.isDeleted(false)
Soundbar91 marked this conversation as resolved.
Show resolved Hide resolved
.lecture(lecture1)
.timetableFrame(frame)
.build();

TimetableLecture timetableLecture2 = TimetableLecture.builder()
.grades("0")
.isDeleted(false)
.lecture(lecture2)
.timetableFrame(frame)
.build();

frame.getTimetableLectures().add(timetableLecture1);
frame.getTimetableLectures().add(timetableLecture2);

return timetableFrameRepositoryV2.save(frame);
}
}
Loading