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 @@ -120,7 +120,7 @@ public void deleteTimetableLecture(Integer userId, Integer timetableLectureId) {
if (!Objects.equals(frame.getUser().getId(), userId)) {
throw AuthorizationException.withDetail("userId: " + userId);
}
timetableLectureRepositoryV2.deleteById(timetableLectureId);
timetableLecture.delete();
entityManager.flush();
} catch (OptimisticLockException e) {
throw new RequestTooFastException("요청이 너무 빠릅니다. 다시 시도해주세요.");
Expand Down
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(
@RequestParam(name = "timetable_lectures_id") 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(
@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,15 @@ public void addTimeTableLecture(TimetableLecture lecture) {
}
timetableLectures.add(lecture);
}

public void delete() {
this.isDeleted = true;
if (timetableLectures != null) {
timetableLectures.forEach(TimetableLecture::delete);
}
}

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,12 @@ public void update(TimetableUpdateRequest.InnerTimetableRequest request) {
this.grades = grades;
this.memo = request.memo();
}

public void delete() {
this.isDeleted = true;
}

public void undelete() {
this.isDeleted = false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,15 @@ SELECT COUNT(t) FROM TimetableFrame t

void deleteById(Integer id);

List<TimetableFrame> findAllByUserAndSemester(User user, Semester semester);

void deleteAllByUser(User user);

void deleteAllByUserAndSemester(User user, Semester semester);
@Query(value = "SELECT * FROM timetable_frame WHERE id = :id", nativeQuery = true)
Optional<TimetableFrame> findByIdWithDeleted(@Param("id") Integer id);

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 @@ -26,11 +26,21 @@ default TimetableLecture getById(Integer id) {

TimetableLecture save(TimetableLecture timetableLecture);

@Modifying
@Query(value = """
DELETE FROM timetable_lecture
WHERE frame_id = :frameId
AND lectures_id = :lectureId
""", nativeQuery = true)
void deleteByFrameIdAndLectureId(@Param("frameId") Integer frameId, @Param("lectureId") Integer lectureId);
Optional<TimetableLecture> findByTimetableFrameIdAndLectureId(Integer frameId, Integer lectureId);

default TimetableLecture getByFrameIdAndLectureId(Integer frameId, Integer lectureId) {
return findByTimetableFrameIdAndLectureId(frameId, lectureId)
.orElseThrow(() -> TimetableLectureNotFoundException.withDetail("frameId: " + frameId + ", lectureId: " + lectureId));
}

@Query(value = "SELECT * FROM timetable_lecture WHERE id = :id", nativeQuery = true)
Optional<TimetableLecture> findByIdWithDeleted(@Param("id") Integer id);

default TimetableLecture getByIdWithDeleted(Integer id) {
return findByIdWithDeleted(id)
.orElseThrow(() -> TimetableLectureNotFoundException.withDetail("id: " + id));
}

@Query(value = "SELECT * FROM timetable_lecture WHERE frame_id = :frameId", nativeQuery = true)
List<TimetableLecture> findAllByFrameIdWithDeleted(@Param("frameId") Integer frameId);
}
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,8 +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;

@Service
Expand Down Expand Up @@ -68,21 +65,22 @@ public List<TimetableFrameResponse> getTimetablesFrame(Integer userId, String se
public void deleteAllTimetablesFrame(Integer userId, String semester) {
User user = userRepository.getById(userId);
Semester timetableSemester = semesterRepositoryV2.getBySemester(semester);
timetableFrameRepositoryV2.deleteAllByUserAndSemester(user, timetableSemester);
timetableFrameRepositoryV2.findAllByUserAndSemester(user, timetableSemester)
.forEach(TimetableFrame::delete);
}

@Transactional
public void deleteTimetablesFrame(Integer userId, Integer frameId) {
TimetableFrame timetableFrame = timetableFrameRepositoryV2.getByIdWithLock(frameId);
validateUserAuthorization(timetableFrame.getUser().getId(), userId);

deleteFrameAndUpdateMainStatusWithLock(frameId, userId, timetableFrame);
deleteFrameAndUpdateMainStatus(userId, timetableFrame);
}

@ConcurrencyGuard(lockName = "deleteFrame")
private void deleteFrameAndUpdateMainStatusWithLock(Integer frameId, Integer userId, TimetableFrame frame) {
timetableFrameRepositoryV2.deleteById(frameId);
private void deleteFrameAndUpdateMainStatus(Integer userId, TimetableFrame frame) {
frame.delete();
if (frame.isMain()) {
frame.updateMainFlag(false);
TimetableFrame nextFrame = timetableFrameRepositoryV2.findNextFirstTimetableFrame(userId,
frame.getSemester().getId());
if (nextFrame != null) {
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,14 +18,17 @@
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 jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import lombok.RequiredArgsConstructor;

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class TimetableLectureService {

@PersistenceContext
private EntityManager entityManager;
private final TimetableLectureRepositoryV2 timetableLectureRepositoryV2;
private final TimetableFrameRepositoryV2 timetableFrameRepositoryV2;
private final TimetableLectureCreator timetableLectureCreator;
Expand Down Expand Up @@ -59,7 +61,7 @@ public void deleteTimetableLecture(Integer userId, Integer timetableLectureId) {
TimetableLecture timetableLecture = timetableLectureRepositoryV2.getById(timetableLectureId);
TimetableFrame timetableFrame = timetableLecture.getTimetableFrame();
validateUserAuthorization(timetableFrame.getUser().getId(), userId);
timetableLectureRepositoryV2.deleteById(timetableLectureId);
timetableLecture.delete();
}

private TimetableLectureResponse getTimetableLectureResponse(Integer userId, TimetableFrame timetableFrame) {
Expand All @@ -73,13 +75,39 @@ public void deleteTimetableLectures(List<Integer> request, Integer userId) {
request.stream()
.map(timetableLectureRepositoryV2::getById)
.peek(lecture -> validateUserAuthorization(lecture.getTimetableFrame().getUser().getId(), userId))
.forEach(lecture -> timetableLectureRepositoryV2.deleteById(lecture.getId()));
.forEach(TimetableLecture::delete);
}

@Transactional
public void deleteTimetableLectureByFrameId(Integer frameId, Integer lectureId, Integer userId) {
TimetableFrame frame = timetableFrameRepositoryV2.getById(frameId);
validateUserAuthorization(frame.getUser().getId(), userId);
timetableLectureRepositoryV2.deleteByFrameIdAndLectureId(frameId, lectureId);
TimetableLecture timetableLecture = timetableLectureRepositoryV2.getByFrameIdAndLectureId(frameId, lectureId);
timetableLecture.delete();
}

@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);
entityManager.flush();
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();

timetableLectureRepositoryV2.findAllByFrameIdWithDeleted(timetableFrame.getId()).stream()
.map(TimetableLecture::getId)
.map(timetableLectureRepositoryV2::getByIdWithDeleted)
.forEach(TimetableLecture::undelete);
entityManager.flush();
return getTimetableLectureResponse(userId, timetableFrame);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -177,8 +177,8 @@ void setup() {
)
.andExpect(status().isNoContent());

assertThat(timetableFrameRepositoryV2.findById(frame1.getId())).isNotPresent();
assertThat(timetableLectureRepositoryV2.findById(frame1.getTimetableLectures().get(1).getId())).isNotPresent();
assertThat(frame1.isDeleted()).isTrue();
assertThat(frame1.getTimetableLectures().get(1).isDeleted()).isTrue();
}

@Test
Expand All @@ -194,7 +194,7 @@ void setup() {
)
.andExpect(status().isNoContent());

assertThat(timetableFrameRepositoryV2.findById(frame1.getId())).isNotPresent();
assertThat(frame1.isDeleted()).isTrue();

TimetableFrame reloadedFrame2 = timetableFrameRepositoryV2.findById(frame2.getId()).orElseThrow();
assertThat(reloadedFrame2.isMain()).isTrue();
Expand Down Expand Up @@ -230,8 +230,8 @@ void setup() {
)
.andExpect(status().isNoContent());

assertThat(timetableFrameRepositoryV2.findById(frame1.getId())).isNotPresent();
assertThat(timetableFrameRepositoryV2.findById(frame2.getId())).isNotPresent();
assertThat(timetableFrameRepositoryV2.findById(frame3.getId())).isNotPresent();
assertThat(frame1.isDeleted()).isTrue();
assertThat(frame2.isDeleted()).isTrue();
assertThat(frame3.isDeleted()).isTrue();
}
}
Loading
Loading