diff --git a/src/main/java/com/back/domain/study/entity/RepeatRule.java b/src/main/java/com/back/domain/study/entity/RepeatRule.java deleted file mode 100644 index 06329667..00000000 --- a/src/main/java/com/back/domain/study/entity/RepeatRule.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.back.domain.study.entity; - -import com.back.global.entity.BaseEntity; -import jakarta.persistence.*; -import lombok.Getter; -import lombok.NoArgsConstructor; - -import java.time.LocalDateTime; - -@Entity -@NoArgsConstructor -@Getter -public class RepeatRule extends BaseEntity { - @OneToOne - @JoinColumn(name = "study_plan_id") - private StudyPlan studyPlan; - - @Enumerated(EnumType.STRING) - private Frequency frequency; - - private int RepeatInterval; - - @Enumerated(EnumType.STRING) - private DayOfWeek byDay; - - private LocalDateTime until; -} diff --git a/src/main/java/com/back/domain/study/entity/StudyPlan.java b/src/main/java/com/back/domain/study/entity/StudyPlan.java deleted file mode 100644 index 427db28d..00000000 --- a/src/main/java/com/back/domain/study/entity/StudyPlan.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.back.domain.study.entity; - -import com.back.domain.user.entity.User; -import com.back.global.entity.BaseEntity; -import jakarta.persistence.*; -import lombok.Getter; -import lombok.NoArgsConstructor; - -import java.time.LocalDateTime; - -@Entity -@NoArgsConstructor -@Getter -public class StudyPlan extends BaseEntity { - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "user_id") - private User user; - - private String subject; - - @Enumerated(EnumType.STRING) - private StudyStatus status; - - private LocalDateTime studyDate; - - private LocalDateTime endDate; - - @Enumerated(EnumType.STRING) - private Color color; - - @Enumerated(EnumType.STRING) - private RepeatType repeatType; - - @OneToOne(mappedBy = "studyPlan") - private RepeatRule repeatRule; -} diff --git a/src/main/java/com/back/domain/study/entity/StudyStatus.java b/src/main/java/com/back/domain/study/entity/StudyStatus.java deleted file mode 100644 index eb953649..00000000 --- a/src/main/java/com/back/domain/study/entity/StudyStatus.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.back.domain.study.entity; - -public enum StudyStatus { - TODO, DONE -} diff --git a/src/main/java/com/back/domain/study/plan/controller/StudyPlanController.java b/src/main/java/com/back/domain/study/plan/controller/StudyPlanController.java new file mode 100644 index 00000000..1745ccd0 --- /dev/null +++ b/src/main/java/com/back/domain/study/plan/controller/StudyPlanController.java @@ -0,0 +1,37 @@ +package com.back.domain.study.plan.controller; + +import com.back.domain.study.plan.dto.StudyPlanCreateRequest; +import com.back.domain.study.plan.dto.StudyPlanResponse; +import com.back.domain.study.plan.service.StudyPlanService; +import com.back.global.common.dto.RsData; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/plans") +public class StudyPlanController { + private final StudyPlanService studyPlanService; + + @PostMapping + public ResponseEntity> createStudyPlan( + // 로그인 유저 정보 받기 @AuthenticationPrincipal CustomUserDetails user, + @RequestBody StudyPlanCreateRequest request) { + //커스텀 디테일 구현 시 사용 int userId = user.getId(); + Long userId = 1L; // 임시로 userId를 1로 설정 + StudyPlanResponse response = studyPlanService.createStudyPlan(userId, request); + return ResponseEntity.ok(RsData.success("학습 계획이 성공적으로 생성되었습니다.", response)); + } + + @DeleteMapping("/{planId}") + public ResponseEntity> deleteStudyPlan(@PathVariable Long planId) { + //studyPlanService.deleteStudyPlan(planId); + return ResponseEntity.ok(RsData.success("학습 계획이 성공적으로 삭제되었습니다.", null)); + } + + + + +} diff --git a/src/main/java/com/back/domain/study/plan/dto/StudyPlanCreateRequest.java b/src/main/java/com/back/domain/study/plan/dto/StudyPlanCreateRequest.java new file mode 100644 index 00000000..5b5bfbcd --- /dev/null +++ b/src/main/java/com/back/domain/study/plan/dto/StudyPlanCreateRequest.java @@ -0,0 +1,43 @@ +package com.back.domain.study.plan.dto; + +import com.back.domain.study.plan.entity.Color; +import com.back.domain.study.plan.entity.Frequency; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +public class StudyPlanCreateRequest { + private String subject; + + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss") + private LocalDateTime startDate; + + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss") + private LocalDateTime endDate; + + private Color color; + + // RepeatRule 중첩 객체 + private RepeatRuleRequest repeatRule; + + @Getter + @Setter + @NoArgsConstructor + @AllArgsConstructor + public static class RepeatRuleRequest { + private Frequency frequency; + private Integer intervalValue; + private String byDay; // "MON" 형태의 문자열 + + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd") + private String untilDate; // "2025-12-31" 형태 + } +} diff --git a/src/main/java/com/back/domain/study/plan/dto/StudyPlanResponse.java b/src/main/java/com/back/domain/study/plan/dto/StudyPlanResponse.java new file mode 100644 index 00000000..1f204e29 --- /dev/null +++ b/src/main/java/com/back/domain/study/plan/dto/StudyPlanResponse.java @@ -0,0 +1,88 @@ +package com.back.domain.study.plan.dto; + +import com.back.domain.study.plan.entity.Color; +import com.back.domain.study.plan.entity.DayOfWeek; +import com.back.domain.study.plan.entity.Frequency; +import com.back.domain.study.plan.entity.StudyPlan; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.time.LocalDateTime; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +public class StudyPlanResponse { + private Long id; + private String subject; + + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss") + private LocalDateTime startDate; + + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss") + private LocalDateTime endDate; + + private Color color; + + private Long parentPlanId; + private List childPlans; + + // RepeatRule 정보 + private RepeatRuleResponse repeatRule; + + @Getter + @Setter + @NoArgsConstructor + @AllArgsConstructor + public static class RepeatRuleResponse { + private Frequency frequency; + private Integer repeatInterval; + private String byDay; // "MON" 형태의 문자열 + + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss") + private LocalDateTime until; + + public RepeatRuleResponse(com.back.domain.study.plan.entity.RepeatRule repeatRule) { + if (repeatRule != null) { + this.frequency = repeatRule.getFrequency(); + this.repeatInterval = repeatRule.getRepeatInterval(); + this.byDay = repeatRule.getByDay(); + this.until = repeatRule.getUntilDate(); + } + } + + // 요일을 리스트로 접근 ("MON,TUE" -> [MON, TUE]) + public List getByDaysList() { + if (byDay == null || byDay.isEmpty()) { + return List.of(); + } + return Arrays.stream(byDay.split(",")) + .map(String::trim) + .map(com.back.domain.study.plan.entity.DayOfWeek::valueOf) + .collect(Collectors.toList()); + } + } + //엔티티를 DTO로 변환하는 생성자 + public StudyPlanResponse(StudyPlan studyPlan) { + if (studyPlan != null) { + this.id = studyPlan.getId(); + this.subject = studyPlan.getSubject(); + this.startDate = studyPlan.getStartDate(); + this.endDate = studyPlan.getEndDate(); + this.color = studyPlan.getColor(); + + + // RepeatRule 변환 + if (studyPlan.getRepeatRule() != null) { + this.repeatRule = new RepeatRuleResponse(studyPlan.getRepeatRule()); + } + } + } +} \ No newline at end of file diff --git a/src/main/java/com/back/domain/study/entity/Color.java b/src/main/java/com/back/domain/study/plan/entity/Color.java similarity index 62% rename from src/main/java/com/back/domain/study/entity/Color.java rename to src/main/java/com/back/domain/study/plan/entity/Color.java index b265ca5f..c074e71e 100644 --- a/src/main/java/com/back/domain/study/entity/Color.java +++ b/src/main/java/com/back/domain/study/plan/entity/Color.java @@ -1,4 +1,4 @@ -package com.back.domain.study.entity; +package com.back.domain.study.plan.entity; public enum Color { RED, ORANGE, YELLOW, GREEN, BLUE, PURPLE, PINK diff --git a/src/main/java/com/back/domain/study/entity/DayOfWeek.java b/src/main/java/com/back/domain/study/plan/entity/DayOfWeek.java similarity index 60% rename from src/main/java/com/back/domain/study/entity/DayOfWeek.java rename to src/main/java/com/back/domain/study/plan/entity/DayOfWeek.java index 244e1a87..8127acd2 100644 --- a/src/main/java/com/back/domain/study/entity/DayOfWeek.java +++ b/src/main/java/com/back/domain/study/plan/entity/DayOfWeek.java @@ -1,4 +1,4 @@ -package com.back.domain.study.entity; +package com.back.domain.study.plan.entity; public enum DayOfWeek { MON, TUE, WED, THU, FRI, SAT, SUN diff --git a/src/main/java/com/back/domain/study/entity/Frequency.java b/src/main/java/com/back/domain/study/plan/entity/Frequency.java similarity index 55% rename from src/main/java/com/back/domain/study/entity/Frequency.java rename to src/main/java/com/back/domain/study/plan/entity/Frequency.java index 820b1e8f..cec35ef4 100644 --- a/src/main/java/com/back/domain/study/entity/Frequency.java +++ b/src/main/java/com/back/domain/study/plan/entity/Frequency.java @@ -1,4 +1,4 @@ -package com.back.domain.study.entity; +package com.back.domain.study.plan.entity; public enum Frequency { DAILY, WEEKLY, MONTHLY diff --git a/src/main/java/com/back/domain/study/plan/entity/RepeatRule.java b/src/main/java/com/back/domain/study/plan/entity/RepeatRule.java new file mode 100644 index 00000000..1d7c7120 --- /dev/null +++ b/src/main/java/com/back/domain/study/plan/entity/RepeatRule.java @@ -0,0 +1,34 @@ +package com.back.domain.study.plan.entity; + +import com.back.global.entity.BaseEntity; +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Entity +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +public class RepeatRule extends BaseEntity { + @OneToOne + @JoinColumn(name = "plan_id", nullable = false) + private StudyPlan studyPlan; + + @Enumerated(EnumType.STRING) + private Frequency frequency; + + @Column(name = "interval_value", nullable = false) + private int RepeatInterval; + + //필요 시 요일 지정. 여러 요일 지정 시 ,로 구분 + //현재는 요일 하나만 지정하는 형태로 구현 + @Column(name = "by_day") + private String byDay; + + private LocalDateTime untilDate; +} diff --git a/src/main/java/com/back/domain/study/entity/RepeatType.java b/src/main/java/com/back/domain/study/plan/entity/RepeatType.java similarity index 50% rename from src/main/java/com/back/domain/study/entity/RepeatType.java rename to src/main/java/com/back/domain/study/plan/entity/RepeatType.java index d12a2a63..ff7238ba 100644 --- a/src/main/java/com/back/domain/study/entity/RepeatType.java +++ b/src/main/java/com/back/domain/study/plan/entity/RepeatType.java @@ -1,5 +1,6 @@ -package com.back.domain.study.entity; +package com.back.domain.study.plan.entity; +//삭제 예정 public enum RepeatType { NONE, DAILY, WEEKLY, MONTHLY } diff --git a/src/main/java/com/back/domain/study/plan/entity/StudyPlan.java b/src/main/java/com/back/domain/study/plan/entity/StudyPlan.java new file mode 100644 index 00000000..f992b165 --- /dev/null +++ b/src/main/java/com/back/domain/study/plan/entity/StudyPlan.java @@ -0,0 +1,47 @@ +package com.back.domain.study.plan.entity; + +import com.back.domain.study.record.entity.StudyRecord; +import com.back.domain.user.entity.User; +import com.back.global.entity.BaseEntity; +import jakarta.persistence.*; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + +@Entity +@NoArgsConstructor +@Getter +@Setter +public class StudyPlan extends BaseEntity { + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "user_id", nullable = false) + private User user; + + @Column(nullable = false, length = 100) + private String subject; + + + @Column(name = "start_date", nullable = false) + private LocalDateTime startDate; + + @Column(name = "end_date", nullable = false) + private LocalDateTime endDate; + + @Enumerated(EnumType.STRING) + private Color color; + + + @OneToOne(mappedBy = "studyPlan",cascade = CascadeType.ALL, fetch = FetchType.LAZY) + private RepeatRule repeatRule; + + @OneToMany(mappedBy = "studyPlan", cascade = CascadeType.ALL, orphanRemoval = true) + private List studyRecords; + + //반복 주기 설정 시 예외 리스트 + @OneToMany(mappedBy = "studyPlan", cascade = CascadeType.ALL, fetch = FetchType.LAZY) + private List exceptions = new ArrayList<>(); +} diff --git a/src/main/java/com/back/domain/study/plan/entity/StudyPlanException.java b/src/main/java/com/back/domain/study/plan/entity/StudyPlanException.java new file mode 100644 index 00000000..5c94a3c1 --- /dev/null +++ b/src/main/java/com/back/domain/study/plan/entity/StudyPlanException.java @@ -0,0 +1,63 @@ +package com.back.domain.study.plan.entity; + +import com.back.global.entity.BaseEntity; +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Entity +@Table(name = "study_plan_exception") +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +//이후 날짜 반복 계획 모두 삭제는 원본 엔티티의 untilDate를 수정해 구현 +//단일 삭제 또는 단일, 이후 모두 수정은 이 엔티티로 구현 +public class StudyPlanException extends BaseEntity { + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "study_plan_id", nullable = false) + private StudyPlan studyPlan; + + // 예외가 발생한 날짜 + @Column(name = "exception_date", nullable = false) + private LocalDateTime exceptionDate; + + //예외 유형 (수정 / 삭제) + @Enumerated(EnumType.STRING) + @Column(nullable = false) + private ExceptionType exceptionType; + + // 적용 범위 (이 날짜만 / 이후 모든 날짜) + @Enumerated(EnumType.STRING) + @Column(name = "apply_scope") + private ApplyScope applyScope; + + // 수정된 내용 (MODIFIED 타입인 경우) + @Column(name = "modified_subject") + private String modifiedSubject; + + @Column(name = "modified_start_date") + private LocalDateTime modifiedStartDate; + + @Column(name = "modified_end_date") + private LocalDateTime modifiedEndDate; + + @Enumerated(EnumType.STRING) + @Column(name = "modified_color") + private Color modifiedColor; + + public enum ExceptionType { + DELETED, // 해당 날짜 삭제 + MODIFIED // 해당 날짜 수정 + } + + public enum ApplyScope { + THIS_ONLY, // 이 날짜만 + FROM_THIS_DATE // 이 날짜부터 이후 모든 날짜 + } +} \ No newline at end of file diff --git a/src/main/java/com/back/domain/study/plan/repository/StudyPlanRepository.java b/src/main/java/com/back/domain/study/plan/repository/StudyPlanRepository.java new file mode 100644 index 00000000..53c35c4c --- /dev/null +++ b/src/main/java/com/back/domain/study/plan/repository/StudyPlanRepository.java @@ -0,0 +1,10 @@ +package com.back.domain.study.plan.repository; + +import com.back.domain.study.plan.entity.StudyPlan; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface StudyPlanRepository extends JpaRepository { + +} diff --git a/src/main/java/com/back/domain/study/plan/service/StudyPlanService.java b/src/main/java/com/back/domain/study/plan/service/StudyPlanService.java new file mode 100644 index 00000000..306bf9d8 --- /dev/null +++ b/src/main/java/com/back/domain/study/plan/service/StudyPlanService.java @@ -0,0 +1,63 @@ +package com.back.domain.study.plan.service; + +import com.back.domain.study.plan.dto.StudyPlanCreateRequest; +import com.back.domain.study.plan.dto.StudyPlanResponse; +import com.back.domain.study.plan.entity.RepeatRule; +import com.back.domain.study.plan.entity.StudyPlan; +import com.back.domain.study.plan.repository.StudyPlanRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class StudyPlanService{ + private final StudyPlanRepository studyPlanRepository; + + @Transactional + public StudyPlanResponse createStudyPlan(Long userId, StudyPlanCreateRequest request) { + + StudyPlan studyPlan = new StudyPlan(); + //studyPlan.setUser(user); + studyPlan.setSubject(request.getSubject()); + studyPlan.setStartDate(request.getStartDate()); + studyPlan.setEndDate(request.getEndDate()); + studyPlan.setColor(request.getColor()); + + // 반복 규칙 설정 + if (request.getRepeatRule() != null) { + StudyPlanCreateRequest.RepeatRuleRequest repeatRuleRequest = request.getRepeatRule(); + RepeatRule repeatRule = new RepeatRule(); + repeatRule.setStudyPlan(studyPlan); + repeatRule.setFrequency(repeatRuleRequest.getFrequency()); + repeatRule.setRepeatInterval(repeatRuleRequest.getIntervalValue() != null ? repeatRuleRequest.getIntervalValue() : 1); + + // byDay 문자열 그대로 저장 + if (repeatRuleRequest.getByDay() != null && !repeatRuleRequest.getByDay().isEmpty()) { + repeatRule.setByDay(repeatRuleRequest.getByDay()); + } + + // untilDate 문자열을 LocalDateTime으로 변환 + if (repeatRuleRequest.getUntilDate() != null && !repeatRuleRequest.getUntilDate().isEmpty()) { + try { + LocalDateTime untilDateTime = LocalDateTime.parse(repeatRuleRequest.getUntilDate() + "T23:59:59"); + repeatRule.setUntilDate(untilDateTime); + } catch (Exception e) { + // 날짜 파싱 실패 시 무시하거나 예외 처리 + } + } + + studyPlan.setRepeatRule(repeatRule); + } + + //추후 변수명이나 리턴 형식은 수정 예정 + StudyPlanResponse rs =new StudyPlanResponse(studyPlanRepository.save(studyPlan)); + return rs; + } + + + +} diff --git a/src/main/java/com/back/domain/study/entity/StudyRecord.java b/src/main/java/com/back/domain/study/record/entity/StudyRecord.java similarity index 79% rename from src/main/java/com/back/domain/study/entity/StudyRecord.java rename to src/main/java/com/back/domain/study/record/entity/StudyRecord.java index ee892c52..100a0110 100644 --- a/src/main/java/com/back/domain/study/entity/StudyRecord.java +++ b/src/main/java/com/back/domain/study/record/entity/StudyRecord.java @@ -1,5 +1,6 @@ -package com.back.domain.study.entity; +package com.back.domain.study.record.entity; +import com.back.domain.study.plan.entity.StudyPlan; import com.back.domain.studyroom.entity.Room; import com.back.global.entity.BaseEntity; import jakarta.persistence.Entity; @@ -16,8 +17,8 @@ @Getter public class StudyRecord extends BaseEntity { @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "todo_id") - private Todo todo; + @JoinColumn(name = "plan_id") + private StudyPlan studyPlan; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "room_id") diff --git a/src/main/java/com/back/domain/study/entity/Todo.java b/src/main/java/com/back/domain/study/todo/entity/Todo.java similarity index 75% rename from src/main/java/com/back/domain/study/entity/Todo.java rename to src/main/java/com/back/domain/study/todo/entity/Todo.java index efafdc08..73c606a2 100644 --- a/src/main/java/com/back/domain/study/entity/Todo.java +++ b/src/main/java/com/back/domain/study/todo/entity/Todo.java @@ -1,5 +1,6 @@ -package com.back.domain.study.entity; +package com.back.domain.study.todo.entity; +import com.back.domain.study.record.entity.StudyRecord; import com.back.domain.user.entity.User; import com.back.global.entity.BaseEntity; import jakarta.persistence.*; @@ -23,6 +24,4 @@ public class Todo extends BaseEntity { private LocalDateTime date; - @OneToMany(mappedBy = "todo", cascade = CascadeType.ALL, orphanRemoval = true) - private List studyRecords; } diff --git a/src/main/java/com/back/domain/studyroom/entity/Room.java b/src/main/java/com/back/domain/studyroom/entity/Room.java index 706f2c90..e07fd5a6 100644 --- a/src/main/java/com/back/domain/studyroom/entity/Room.java +++ b/src/main/java/com/back/domain/studyroom/entity/Room.java @@ -1,6 +1,6 @@ package com.back.domain.studyroom.entity; -import com.back.domain.study.entity.StudyRecord; +import com.back.domain.study.record.entity.StudyRecord; import com.back.domain.user.entity.User; import com.back.global.entity.BaseEntity; import jakarta.persistence.*; diff --git a/src/main/java/com/back/domain/user/entity/User.java b/src/main/java/com/back/domain/user/entity/User.java index a08fb0b3..554aa564 100644 --- a/src/main/java/com/back/domain/user/entity/User.java +++ b/src/main/java/com/back/domain/user/entity/User.java @@ -2,8 +2,8 @@ import com.back.domain.board.entity.*; import com.back.domain.file.entity.FileAttachment; -import com.back.domain.study.entity.StudyPlan; -import com.back.domain.study.entity.Todo; +import com.back.domain.study.plan.entity.StudyPlan; +import com.back.domain.study.todo.entity.Todo; import com.back.domain.studyroom.entity.RoomChatMessage; import com.back.domain.studyroom.entity.RoomMember; import com.back.domain.studyroom.entity.RoomParticipantHistory;