diff --git a/src/main/java/inu/timetable/controller/TimetableController.java b/src/main/java/inu/timetable/controller/TimetableController.java index 1a1cb67..befc427 100644 --- a/src/main/java/inu/timetable/controller/TimetableController.java +++ b/src/main/java/inu/timetable/controller/TimetableController.java @@ -33,9 +33,9 @@ public ResponseEntity addSubjectToTimetable(@RequestBody Map } @DeleteMapping("/remove") - public ResponseEntity removeSubjectFromTimetable(@RequestParam Long userId, @RequestParam Long subjectId) { + public ResponseEntity removeSubjectFromTimetable(@RequestParam Long userId, @RequestParam Long subjectId, @RequestParam String semester) { try { - timetableService.removeSubjectFromTimetable(userId, subjectId); + timetableService.removeSubjectFromTimetable(userId, subjectId, semester); return ResponseEntity.ok(Map.of("message", "과목이 시간표에서 제거되었습니다.")); } catch (RuntimeException e) { @@ -46,7 +46,7 @@ public ResponseEntity removeSubjectFromTimetable(@RequestParam Long userId, @ @GetMapping("/user/{userId}") public ResponseEntity> getUserTimetable( @PathVariable Long userId, - @RequestParam(required = false) String semester) { + @RequestParam String semester) { List timetable = timetableService.getUserTimetable(userId, semester); return ResponseEntity.ok(timetable); @@ -57,9 +57,10 @@ public ResponseEntity updateMemo(@RequestBody Map request) { try { Long userId = Long.valueOf(request.get("userId").toString()); Long subjectId = Long.valueOf(request.get("subjectId").toString()); + String semester = (String) request.get("semester"); String memo = (String) request.get("memo"); - UserTimetable userTimetable = timetableService.updateMemo(userId, subjectId, memo); + UserTimetable userTimetable = timetableService.updateMemo(userId, subjectId, semester, memo); return ResponseEntity.ok(userTimetable); } catch (RuntimeException e) { @@ -68,7 +69,7 @@ public ResponseEntity updateMemo(@RequestBody Map request) { } @DeleteMapping("/clear") - public ResponseEntity clearTimetable(@RequestParam Long userId, @RequestParam(required = false) String semester) { + public ResponseEntity clearTimetable(@RequestParam Long userId, @RequestParam String semester) { try { timetableService.removeAllSubjectsFromTimetable(userId, semester); String message = semester != null && !semester.isEmpty() diff --git a/src/main/java/inu/timetable/controller/WishlistController.java b/src/main/java/inu/timetable/controller/WishlistController.java index ee34af7..4b41e59 100644 --- a/src/main/java/inu/timetable/controller/WishlistController.java +++ b/src/main/java/inu/timetable/controller/WishlistController.java @@ -38,9 +38,9 @@ public ResponseEntity addToWishlist(@RequestBody Map request) } @DeleteMapping("/remove") - public ResponseEntity removeFromWishlist(@RequestParam Long userId, @RequestParam Long subjectId) { + public ResponseEntity removeFromWishlist(@RequestParam Long userId, @RequestParam Long subjectId, @RequestParam String semester) { try { - wishlistService.removeFromWishlist(userId, subjectId); + wishlistService.removeFromWishlist(userId, subjectId, semester); return ResponseEntity.ok(Map.of("message", "위시리스트에서 제거되었습니다.")); } catch (RuntimeException e) { @@ -75,9 +75,10 @@ public ResponseEntity updatePriority(@RequestBody Map request try { Long userId = Long.valueOf(request.get("userId").toString()); Long subjectId = Long.valueOf(request.get("subjectId").toString()); + String semester = (String) request.get("semester"); Integer priority = Integer.valueOf(request.get("priority").toString()); - wishlistService.updatePriority(userId, subjectId, priority); + wishlistService.updatePriority(userId, subjectId, semester, priority); return ResponseEntity.ok(Map.of("message", "우선순위가 업데이트되었습니다.")); } catch (RuntimeException e) { @@ -90,9 +91,10 @@ public ResponseEntity updateRequired(@RequestBody Map request try { Long userId = Long.valueOf(request.get("userId").toString()); Long subjectId = Long.valueOf(request.get("subjectId").toString()); + String semester = (String) request.get("semester"); Boolean isRequired = Boolean.valueOf(request.get("isRequired").toString()); - wishlistService.updateRequired(userId, subjectId, isRequired); + wishlistService.updateRequired(userId, subjectId, semester, isRequired); return ResponseEntity.ok(Map.of("message", "필수 과목 설정이 업데이트되었습니다.")); } catch (RuntimeException e) { diff --git a/src/main/java/inu/timetable/repository/UserTimetableRepository.java b/src/main/java/inu/timetable/repository/UserTimetableRepository.java index 2a18fed..6b94283 100644 --- a/src/main/java/inu/timetable/repository/UserTimetableRepository.java +++ b/src/main/java/inu/timetable/repository/UserTimetableRepository.java @@ -2,6 +2,7 @@ import inu.timetable.entity.UserTimetable; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; @@ -16,8 +17,12 @@ public interface UserTimetableRepository extends JpaRepository findByUserId(Long userId); - Optional findByUserIdAndSubjectId(Long userId, Long subjectId); + Optional findByUserIdAndSubjectIdAndSemester(Long userId, Long subjectId, String semester); + @Modifying(clearAutomatically = true) + @Query("DELETE FROM UserTimetable ut WHERE ut.user.id = :userId AND ut.subject.id = :subjectId AND ut.semester = :semester") + int deleteByUserIdAndSubjectIdAndSemester(@Param("userId") Long userId, @Param("subjectId") Long subjectId, @Param("semester") String semester); + @Query("SELECT DISTINCT ut FROM UserTimetable ut JOIN FETCH ut.subject s WHERE ut.user.id = :userId AND ut.semester = :semester") List findByUserIdAndSemesterWithSubjectAndSchedules(@Param("userId") Long userId, @Param("semester") String semester); } \ No newline at end of file diff --git a/src/main/java/inu/timetable/repository/WishlistRepository.java b/src/main/java/inu/timetable/repository/WishlistRepository.java index 51efa9d..19e0529 100644 --- a/src/main/java/inu/timetable/repository/WishlistRepository.java +++ b/src/main/java/inu/timetable/repository/WishlistRepository.java @@ -22,10 +22,10 @@ public interface WishlistRepository extends JpaRepository { "ORDER BY w.priority") List findByUserIdAndSemesterWithSubjectAndSchedules(@Param("userId") Long userId, @Param("semester") String semester); - @Query("SELECT w FROM WishlistItem w JOIN FETCH w.subject s WHERE w.user.id = :userId AND s.id = :subjectId") - Optional findByUserIdAndSubjectId(@Param("userId") Long userId, @Param("subjectId") Long subjectId); + @Query("SELECT w FROM WishlistItem w JOIN FETCH w.subject s WHERE w.user.id = :userId AND s.id = :subjectId AND w.semester = :semester") + Optional findByUserIdAndSubjectIdAndSemester(@Param("userId") Long userId, @Param("subjectId") Long subjectId, @Param("semester") String semester); - @Modifying - @Query("DELETE FROM WishlistItem w WHERE w.user.id = :userId AND w.subject.id = :subjectId") - void deleteByUserIdAndSubjectId(@Param("userId") Long userId, @Param("subjectId") Long subjectId); + @Modifying(clearAutomatically = true) + @Query("DELETE FROM WishlistItem w WHERE w.user.id = :userId AND w.subject.id = :subjectId AND w.semester = :semester") + int deleteByUserIdAndSubjectIdAndSemester(@Param("userId") Long userId, @Param("subjectId") Long subjectId, @Param("semester") String semester); } \ No newline at end of file diff --git a/src/main/java/inu/timetable/service/TimetableService.java b/src/main/java/inu/timetable/service/TimetableService.java index 7ab94ce..0b0cfcb 100644 --- a/src/main/java/inu/timetable/service/TimetableService.java +++ b/src/main/java/inu/timetable/service/TimetableService.java @@ -8,12 +8,20 @@ import inu.timetable.repository.UserTimetableRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import java.util.List; import java.util.Optional; @Service public class TimetableService { + + private String requireSemester(String semester) { + if (semester == null || semester.isBlank()) { + throw new RuntimeException("학기(semester)는 필수입니다."); + } + return semester; + } private final UserTimetableRepository userTimetableRepository; private final UserRepository userRepository; @@ -36,13 +44,14 @@ public UserTimetable addSubjectToTimetable(Long userId, Long subjectId, String s .orElseThrow(() -> new RuntimeException("과목을 찾을 수 없습니다.")); // 이미 추가된 과목인지 확인 - Optional existing = userTimetableRepository.findByUserIdAndSubjectId(userId, subjectId); + String sem = requireSemester(semester); + Optional existing = userTimetableRepository.findByUserIdAndSubjectIdAndSemester(userId, subjectId, sem); if (existing.isPresent()) { throw new RuntimeException("이미 시간표에 추가된 과목입니다."); } // 시간표 겹침 확인 - List currentTimetable = userTimetableRepository.findByUserIdAndSemesterWithSubjectAndSchedules(userId, semester); + List currentTimetable = userTimetableRepository.findByUserIdAndSemesterWithSubjectAndSchedules(userId, sem); if (hasTimeConflict(currentTimetable, subject)) { throw new RuntimeException("시간표가 겹치는 과목이 있습니다."); } @@ -50,18 +59,20 @@ public UserTimetable addSubjectToTimetable(Long userId, Long subjectId, String s UserTimetable userTimetable = UserTimetable.builder() .user(user) .subject(subject) - .semester(semester) + .semester(sem) .memo(memo) .build(); return userTimetableRepository.save(userTimetable); } - public void removeSubjectFromTimetable(Long userId, Long subjectId) { - UserTimetable userTimetable = userTimetableRepository.findByUserIdAndSubjectId(userId, subjectId) - .orElseThrow(() -> new RuntimeException("시간표에서 해당 과목을 찾을 수 없습니다.")); - - userTimetableRepository.delete(userTimetable); + @Transactional + public void removeSubjectFromTimetable(Long userId, Long subjectId, String semester) { + String sem = requireSemester(semester); + int deleted = userTimetableRepository.deleteByUserIdAndSubjectIdAndSemester(userId, subjectId, sem); + if (deleted == 0) { + throw new RuntimeException("시간표에서 해당 과목을 찾을 수 없습니다."); + } } public List getUserTimetable(Long userId, String semester) { @@ -72,8 +83,9 @@ public List getUserTimetable(Long userId, String semester) { } } - public UserTimetable updateMemo(Long userId, Long subjectId, String memo) { - UserTimetable userTimetable = userTimetableRepository.findByUserIdAndSubjectId(userId, subjectId) + public UserTimetable updateMemo(Long userId, Long subjectId, String semester, String memo) { + String sem = requireSemester(semester); + UserTimetable userTimetable = userTimetableRepository.findByUserIdAndSubjectIdAndSemester(userId, subjectId, sem) .orElseThrow(() -> new RuntimeException("시간표에서 해당 과목을 찾을 수 없습니다.")); userTimetable.setMemo(memo); diff --git a/src/main/java/inu/timetable/service/WishlistService.java b/src/main/java/inu/timetable/service/WishlistService.java index 9d446a0..815fb5c 100644 --- a/src/main/java/inu/timetable/service/WishlistService.java +++ b/src/main/java/inu/timetable/service/WishlistService.java @@ -15,6 +15,13 @@ @Service public class WishlistService { + + private String requireSemester(String semester) { + if (semester == null || semester.isBlank()) { + throw new RuntimeException("학기(semester)는 필수입니다."); + } + return semester; + } private final WishlistRepository wishlistRepository; private final UserRepository userRepository; @@ -41,7 +48,8 @@ public WishlistItem addToWishlist(Long userId, Long subjectId, String semester, .orElseThrow(() -> new RuntimeException("과목을 찾을 수 없습니다.")); // 이미 위시리스트에 있는지 확인 - Optional existing = wishlistRepository.findByUserIdAndSubjectId(userId, subjectId); + String sem = requireSemester(semester); + Optional existing = wishlistRepository.findByUserIdAndSubjectIdAndSemester(userId, subjectId, sem); if (existing.isPresent()) { throw new RuntimeException("이미 위시리스트에 추가된 과목입니다."); } @@ -49,7 +57,7 @@ public WishlistItem addToWishlist(Long userId, Long subjectId, String semester, WishlistItem wishlistItem = WishlistItem.builder() .user(user) .subject(subject) - .semester(semester) + .semester(sem) .priority(priority != null ? priority : 3) // 기본 우선순위: 중간 .isRequired(isRequired != null ? isRequired : false) .build(); @@ -58,24 +66,30 @@ public WishlistItem addToWishlist(Long userId, Long subjectId, String semester, } @Transactional - public void removeFromWishlist(Long userId, Long subjectId) { - wishlistRepository.deleteByUserIdAndSubjectId(userId, subjectId); + public void removeFromWishlist(Long userId, Long subjectId, String semester) { + String sem = requireSemester(semester); + int deleted = wishlistRepository.deleteByUserIdAndSubjectIdAndSemester(userId, subjectId, sem); + if (deleted == 0) { + throw new RuntimeException("위시리스트에서 해당 과목을 찾을 수 없습니다."); + } } public List getUserWishlist(Long userId, String semester) { return wishlistRepository.findByUserIdAndSemesterWithSubjectAndSchedules(userId, semester); } - public WishlistItem updatePriority(Long userId, Long subjectId, Integer priority) { - WishlistItem wishlistItem = wishlistRepository.findByUserIdAndSubjectId(userId, subjectId) + public WishlistItem updatePriority(Long userId, Long subjectId, String semester, Integer priority) { + String sem = requireSemester(semester); + WishlistItem wishlistItem = wishlistRepository.findByUserIdAndSubjectIdAndSemester(userId, subjectId, sem) .orElseThrow(() -> new RuntimeException("위시리스트에서 해당 과목을 찾을 수 없습니다.")); wishlistItem.setPriority(priority); return wishlistRepository.save(wishlistItem); } - public WishlistItem updateRequired(Long userId, Long subjectId, Boolean isRequired) { - WishlistItem wishlistItem = wishlistRepository.findByUserIdAndSubjectId(userId, subjectId) + public WishlistItem updateRequired(Long userId, Long subjectId, String semester, Boolean isRequired) { + String sem = requireSemester(semester); + WishlistItem wishlistItem = wishlistRepository.findByUserIdAndSubjectIdAndSemester(userId, subjectId, sem) .orElseThrow(() -> new RuntimeException("위시리스트에서 해당 과목을 찾을 수 없습니다.")); wishlistItem.setIsRequired(isRequired);