Skip to content

Commit 564b0e6

Browse files
authored
Fix,Refect: 수정에도 byDay 불러오기 + 수정,삭제 기능 개선 (#135) (#139)
* fix : byDay 자동 입력 수정에도 적용 + refect: byDay 타입 변경 및 일부 중복 제거 * refect: 삭제 시 삭제된 계획 정보 반환 * test: 삭제 테스트 수정 * docs: 주석 수정 * fix: dto 어노테이션 누락 해결
1 parent ee333c6 commit 564b0e6

File tree

9 files changed

+162
-101
lines changed

9 files changed

+162
-101
lines changed

src/main/java/com/back/domain/study/plan/controller/StudyPlanController.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.back.domain.study.plan.controller;
22

3+
import com.back.domain.study.plan.dto.StudyPlanDeleteResponse;
34
import com.back.domain.study.plan.dto.StudyPlanRequest;
45
import com.back.domain.study.plan.dto.StudyPlanListResponse;
56
import com.back.domain.study.plan.dto.StudyPlanResponse;
@@ -111,15 +112,15 @@ public ResponseEntity<RsData<StudyPlanResponse>> updateStudyPlan(
111112
description = "기존 학습 계획을 삭제합니다. 반복 계획의 경우 적용 범위를 applyScope로 설정 할 수 있으며" +
112113
"클라이언트에서는 paln에 repeat_rule이 있으면 반복 계획으로 간주하고 반드시 apply_scope를 쿼리 파라미터로 넘겨야 합니다." +
113114
"repeat_rule이 없으면 단발성 계획으로 간주하여 삭제 범위를 설정 할 필요가 없으므로 apply_scope를 넘기지 않아도 됩니다.")
114-
public ResponseEntity<RsData<Void>> deleteStudyPlan(
115+
public ResponseEntity<RsData<StudyPlanDeleteResponse>> deleteStudyPlan(
115116
@AuthenticationPrincipal CustomUserDetails user,
116117
@PathVariable Long planId,
117118
@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate selectedDate,
118119
@RequestParam(name = "applyScope", required = true) ApplyScope applyScope) {
119120
Long userId = user.getUserId();
120121

121-
studyPlanService.deleteStudyPlan(userId, planId, selectedDate, applyScope);
122-
return ResponseEntity.ok(RsData.success("학습 계획이 성공적으로 삭제되었습니다."));
122+
StudyPlanDeleteResponse response = studyPlanService.deleteStudyPlan(userId, planId, selectedDate, applyScope);
123+
return ResponseEntity.ok(RsData.success("학습 계획이 성공적으로 삭제되었습니다.",response));
123124
}
124125

125126
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package com.back.domain.study.plan.dto;
2+
3+
import com.back.domain.study.plan.entity.ApplyScope;
4+
import com.back.domain.study.plan.entity.Color;
5+
import lombok.AllArgsConstructor;
6+
import lombok.Getter;
7+
import lombok.NoArgsConstructor;
8+
9+
import java.time.LocalDate;
10+
import java.time.LocalDateTime;
11+
12+
@Getter
13+
@NoArgsConstructor
14+
@AllArgsConstructor
15+
public class StudyPlanDeleteResponse {
16+
private Long id;
17+
private String subject;
18+
private LocalDateTime startDate;
19+
private LocalDateTime endDate;
20+
private Color color;
21+
private LocalDate deletedDate; // 삭제된 날짜
22+
private ApplyScope applyScope; // 삭제 범위
23+
24+
public StudyPlanDeleteResponse(StudyPlanResponse plan, ApplyScope applyScope) {
25+
this.id = plan.getId();
26+
this.subject = plan.getSubject();
27+
this.startDate = plan.getStartDate();
28+
this.endDate = plan.getEndDate();
29+
this.color = plan.getColor();
30+
this.deletedDate = plan.getStartDate().toLocalDate();
31+
this.applyScope = applyScope;
32+
}
33+
}

src/main/java/com/back/domain/study/plan/dto/StudyPlanRequest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.back.domain.study.plan.dto;
22

33
import com.back.domain.study.plan.entity.Color;
4+
import com.back.domain.study.plan.entity.DayOfWeek;
45
import com.back.domain.study.plan.entity.Frequency;
56
import com.fasterxml.jackson.annotation.JsonFormat;
67
import lombok.AllArgsConstructor;
@@ -37,7 +38,7 @@ public class StudyPlanRequest {
3738
public static class RepeatRuleRequest {
3839
private Frequency frequency;
3940
private Integer intervalValue;
40-
private List<String> byDay = new ArrayList<>(); // 문자열 리스트
41+
private List<DayOfWeek> byDay = new ArrayList<>();
4142

4243
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
4344
private String untilDate; // "2025-12-31" 형태

src/main/java/com/back/domain/study/plan/dto/StudyPlanResponse.java

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,7 @@ public class StudyPlanResponse {
4343
public static class RepeatRuleResponse {
4444
private Frequency frequency;
4545
private Integer repeatInterval;
46-
// byDay 필드는 이미 List<String>으로 선언되어 있음.
47-
private List<String> byDay = new ArrayList<>(); // "MON" 형태의 문자열 리스트
46+
private List<DayOfWeek> byDay = new ArrayList<>(); // "MON" 형태의 enum 문자열 리스트
4847

4948
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
5049
private LocalDate untilDate;
@@ -60,16 +59,6 @@ public RepeatRuleResponse(com.back.domain.study.plan.entity.RepeatRule repeatRul
6059
}
6160
}
6261

63-
public List<DayOfWeek> getByDaysList() {
64-
if (byDay == null || byDay.isEmpty()) {
65-
return List.of();
66-
}
67-
68-
// List<String>의 각 요소를 DayOfWeek enum으로 변환하여 반환
69-
return byDay.stream()
70-
.map(com.back.domain.study.plan.entity.DayOfWeek::valueOf)
71-
.collect(Collectors.toList());
72-
}
7362
}
7463
//엔티티를 DTO로 변환하는 생성자
7564
public StudyPlanResponse(StudyPlan studyPlan) {

src/main/java/com/back/domain/study/plan/entity/RepeatRule.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public class RepeatRule extends BaseEntity {
2929

3030
//요일은 응답에 들어있는 요일을 그대로 저장 (예: "WED")
3131
@Column(name = "by_day")
32-
private List<String> byDay = new ArrayList<>();
32+
private List<DayOfWeek> byDay = new ArrayList<>();
3333

3434
private LocalDate untilDate;
3535
}

src/main/java/com/back/domain/study/plan/entity/RepeatRuleEmbeddable.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,6 @@ public class RepeatRuleEmbeddable {
2222
private Frequency frequency;
2323

2424
private Integer intervalValue;
25-
private List<String> byDay = new ArrayList<>();
25+
private List<DayOfWeek> byDay = new ArrayList<>();
2626
private LocalDate untilDate; // LocalDateTime → LocalDate 변경
2727
}

src/main/java/com/back/domain/study/plan/entity/RepeatType.java

Lines changed: 0 additions & 6 deletions
This file was deleted.

src/main/java/com/back/domain/study/plan/service/StudyPlanService.java

Lines changed: 75 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package com.back.domain.study.plan.service;
22

3-
import com.back.domain.study.plan.dto.StudyPlanDeleteRequest;
3+
import com.back.domain.study.plan.dto.StudyPlanDeleteResponse;
44
import com.back.domain.study.plan.dto.StudyPlanRequest;
55
import com.back.domain.study.plan.dto.StudyPlanResponse;
66
import com.back.domain.study.plan.entity.*;
@@ -69,19 +69,9 @@ private RepeatRule createRepeatRule(StudyPlanRequest.RepeatRuleRequest request,
6969
repeatRule.setStudyPlan(studyPlan);
7070
repeatRule.setFrequency(request.getFrequency());
7171
repeatRule.setRepeatInterval(request.getIntervalValue() != null ? request.getIntervalValue() : 1);
72+
// byDay 설정 (WEEKLY인 경우에만 의미 있음)
73+
getByDayInWeekly(request, studyPlan, repeatRule);
7274

73-
// byDay 설정 (WEEKLY 인 경우에만)
74-
if (request.getFrequency() == Frequency.WEEKLY) {
75-
// 1. byDay가 없으면 시작일 요일을 자동으로 설정 (현재 구현 의도 반영)
76-
if(request.getByDay() == null || request.getByDay().isEmpty()) {
77-
String startDayOfWeek = studyPlan.getStartDate().getDayOfWeek().name().substring(0, 3);
78-
// *가정: RepeatRule.byDay는 List<String> 타입으로 가정
79-
repeatRule.setByDay(List.of(startDayOfWeek));
80-
} else {
81-
// 2. byDay가 있다면 요청 값을 사용 (List<String> to List<String> 매핑 확인)
82-
repeatRule.setByDay(request.getByDay());
83-
}
84-
}
8575
// untilDate 설정 및 검증
8676
LocalDate untilDate;
8777

@@ -335,6 +325,10 @@ public StudyPlanResponse updateStudyPlan(Long userId, Long planId, StudyPlanRequ
335325

336326
switch (updateType) {
337327
case ORIGINAL_PLAN_UPDATE:
328+
// 요청에 반복 규칙이 있으면 반복 규칙 수정 후 원본 계획 수정
329+
if (request.getRepeatRule() != null) {
330+
updateRepeatRule(originalPlan.getRepeatRule(), request.getRepeatRule(), originalPlan);
331+
}
338332
return updateOriginalPlan(originalPlan, request);
339333

340334
case REPEAT_INSTANCE_CREATE:
@@ -390,11 +384,6 @@ private StudyPlanResponse updateOriginalPlan(StudyPlan originalPlan, StudyPlanRe
390384
if (request.getEndDate() != null) originalPlan.setEndDate(request.getEndDate());
391385
if (request.getColor() != null) originalPlan.setColor(request.getColor());
392386

393-
// 요청에 반복 규칙이 있고 원본 반복성 계획인 경우에만 반복 규칙 수정
394-
if (request.getRepeatRule() != null && originalPlan.getRepeatRule() != null) {
395-
updateRepeatRule(originalPlan.getRepeatRule(), request.getRepeatRule());
396-
}
397-
398387
StudyPlan savedPlan = studyPlanRepository.save(originalPlan);
399388
return new StudyPlanResponse(savedPlan);
400389
}
@@ -412,7 +401,7 @@ private StudyPlanResponse createRepeatException(StudyPlan originalPlan, StudyPla
412401
exception.setStudyPlan(originalPlan);
413402
exception.setExceptionDate(exceptionDate);
414403
exception.setExceptionType(StudyPlanException.ExceptionType.MODIFIED);
415-
exception.setApplyScope(applyScope); // 파라미터로 받은 applyScope
404+
exception.setApplyScope(applyScope);
416405

417406
// 수정된 내용 설정
418407
if (request.getSubject() != null) exception.setModifiedSubject(request.getSubject());
@@ -422,24 +411,10 @@ private StudyPlanResponse createRepeatException(StudyPlan originalPlan, StudyPla
422411

423412
// 반복 규칙 수정. 요청에 반복 규칙이 있으면 설정
424413
if (request.getRepeatRule() != null) {
425-
RepeatRuleEmbeddable embeddable = new RepeatRuleEmbeddable();
426-
embeddable.setFrequency(request.getRepeatRule().getFrequency());
427-
embeddable.setIntervalValue(request.getRepeatRule().getIntervalValue());
428-
embeddable.setByDay(request.getRepeatRule().getByDay());
429-
430-
if (request.getRepeatRule().getUntilDate() != null && !request.getRepeatRule().getUntilDate().isEmpty()) {
431-
try {
432-
LocalDate untilDate = LocalDate.parse(request.getRepeatRule().getUntilDate());
433-
embeddable.setUntilDate(untilDate);
434-
} catch (Exception e) {
435-
throw new CustomException(ErrorCode.INVALID_DATE_FORMAT);
436-
}
437-
}
438-
414+
RepeatRuleEmbeddable embeddable = createRepeatRuleEmbeddable(request.getRepeatRule(), request.getStartDate());
439415
exception.setModifiedRepeatRule(embeddable);
440416
}
441417

442-
443418
studyPlanExceptionRepository.save(exception);
444419
return createVirtualPlanForDate(originalPlan, exceptionDate);
445420
}
@@ -458,38 +433,26 @@ private StudyPlanResponse updateExistingException(StudyPlan originalPlan, StudyP
458433
if (request.getEndDate() != null) existingException.setModifiedEndDate(request.getEndDate());
459434
if (request.getColor() != null) existingException.setModifiedColor(request.getColor());
460435

461-
// ApplyScope도 업데이트 (사용자가 범위를 변경할 수 있음)
436+
// ApplyScope도 업데이트
462437
existingException.setApplyScope(applyScope);
463438

464439
// 반복 규칙 수정사항 있으면 예외 안에 추가 (embeddable)
465440
if (request.getRepeatRule() != null) {
466-
RepeatRuleEmbeddable embeddable = new RepeatRuleEmbeddable();
467-
embeddable.setFrequency(request.getRepeatRule().getFrequency());
468-
embeddable.setIntervalValue(request.getRepeatRule().getIntervalValue());
469-
embeddable.setByDay(request.getRepeatRule().getByDay());
470-
471-
if (request.getRepeatRule().getUntilDate() != null && !request.getRepeatRule().getUntilDate().isEmpty()) {
472-
try {
473-
LocalDate untilDate = LocalDate.parse(request.getRepeatRule().getUntilDate());
474-
embeddable.setUntilDate(untilDate);
475-
} catch (Exception e) {
476-
throw new CustomException(ErrorCode.INVALID_DATE_FORMAT);
477-
}
478-
}
479-
441+
RepeatRuleEmbeddable embeddable = createRepeatRuleEmbeddable(request.getRepeatRule(), request.getStartDate());
480442
existingException.setModifiedRepeatRule(embeddable);
481443
}
482444

483445
studyPlanExceptionRepository.save(existingException);
484446
return createVirtualPlanForDate(originalPlan, exceptionDate);
485447
}
486448

487-
488449
// 원본의 반복 룰 수정 (엔티티)
489-
private void updateRepeatRule(RepeatRule repeatRule, StudyPlanRequest.RepeatRuleRequest request) {
450+
private void updateRepeatRule(RepeatRule repeatRule, StudyPlanRequest.RepeatRuleRequest request, StudyPlan studyPlan) {
490451
if (request.getFrequency() != null) repeatRule.setFrequency(request.getFrequency());
491452
if (request.getIntervalValue() != null) repeatRule.setRepeatInterval(request.getIntervalValue());
492-
if (request.getByDay() != null) repeatRule.setByDay(request.getByDay());
453+
454+
// byDay 자동 설정 (기존 메서드 재사용)
455+
getByDayInWeekly(request, studyPlan, repeatRule);
493456

494457
if (request.getUntilDate() != null && !request.getUntilDate().isEmpty()) {
495458
try {
@@ -501,22 +464,52 @@ private void updateRepeatRule(RepeatRule repeatRule, StudyPlanRequest.RepeatRule
501464
}
502465
}
503466

467+
// RepeatRuleEmbeddable 생성 헬퍼 메서드 (중복 코드 제거)
468+
private RepeatRuleEmbeddable createRepeatRuleEmbeddable(StudyPlanRequest.RepeatRuleRequest request, LocalDateTime startDate) {
469+
RepeatRuleEmbeddable embeddable = new RepeatRuleEmbeddable();
470+
embeddable.setFrequency(request.getFrequency());
471+
embeddable.setIntervalValue(request.getIntervalValue());
472+
473+
// byDay 자동 설정 (오버로딩된 메서드 사용)
474+
getByDayInWeekly(request, startDate, embeddable);
475+
476+
if (request.getUntilDate() != null && !request.getUntilDate().isEmpty()) {
477+
try {
478+
LocalDate untilDate = LocalDate.parse(request.getUntilDate());
479+
embeddable.setUntilDate(untilDate);
480+
} catch (Exception e) {
481+
throw new CustomException(ErrorCode.INVALID_DATE_FORMAT);
482+
}
483+
}
484+
485+
return embeddable;
486+
}
487+
504488
// ==================== 삭제 ===================
505489
@Transactional
506-
public void deleteStudyPlan(Long userId, Long planId, LocalDate selectedDate, ApplyScope applyScope) {
490+
public StudyPlanDeleteResponse deleteStudyPlan(Long userId, Long planId, LocalDate selectedDate, ApplyScope applyScope) {
507491
StudyPlan studyPlan = studyPlanRepository.findById(planId)
508492
.orElseThrow(() -> new CustomException(ErrorCode.PLAN_NOT_FOUND));
509493

510494
validateUserAccess(studyPlan, userId);
511495

512-
// 단발성 계획 삭제 (반복 룰이 null이거나 applyScope가 null인 경우)
496+
// 삭제 전 정보 조회
497+
StudyPlanResponse deletedPlan;
498+
513499
if (studyPlan.getRepeatRule() == null || applyScope == null) {
500+
// 단발성 계획
501+
deletedPlan = new StudyPlanResponse(studyPlan);
514502
studyPlanRepository.delete(studyPlan);
515-
return;
503+
} else {
504+
// 반복성 계획 - 가상 계획 조회
505+
deletedPlan = createVirtualPlanForDate(studyPlan, selectedDate);
506+
if (deletedPlan == null) {
507+
throw new CustomException(ErrorCode.PLAN_NOT_FOUND);
508+
}
509+
deleteRepeatPlan(studyPlan, selectedDate, applyScope);
516510
}
517511

518-
// 반복성 계획 삭제 - applyScope에 따른 처리
519-
deleteRepeatPlan(studyPlan, selectedDate, applyScope);
512+
return new StudyPlanDeleteResponse(deletedPlan, applyScope);
520513
}
521514

522515
private void deleteRepeatPlan(StudyPlan studyPlan, LocalDate selectedDate, ApplyScope applyScope) {
@@ -617,5 +610,30 @@ private void validateRepeatRuleDate(StudyPlan studyPlan, LocalDate untilDate) {
617610
throw new CustomException(ErrorCode.REPEAT_INVALID_UNTIL_DATE);
618611
}
619612
}
613+
// WEEKLY인 경우 빈 byDay 처리 메서드 (RepeatRule용)
614+
private void getByDayInWeekly(StudyPlanRequest.RepeatRuleRequest request, StudyPlan studyPlan, RepeatRule repeatRule) {
615+
// byDay 설정 (WEEKLY 인 경우에만)
616+
if (request.getFrequency() == Frequency.WEEKLY) {
617+
// 1. byDay가 없으면 시작일 요일을 자동으로 설정
618+
if(request.getByDay() == null || request.getByDay().isEmpty()) {
619+
DayOfWeek startDay = DayOfWeek.valueOf(studyPlan.getStartDate().getDayOfWeek().name().substring(0,3));
620+
repeatRule.setByDay(List.of(startDay));
621+
} else {
622+
// 2. byDay가 있다면 요청 값을 사용
623+
repeatRule.setByDay(request.getByDay());
624+
}
625+
}
626+
}
627+
// WEEKLY인 경우 빈 byDay 처리 메서드 (RepeatRuleEmbeddable용 - 오버로딩)
628+
private void getByDayInWeekly(StudyPlanRequest.RepeatRuleRequest request, LocalDateTime startDate, RepeatRuleEmbeddable embeddable) {
629+
if (request.getFrequency() == Frequency.WEEKLY) {
630+
if (request.getByDay() == null || request.getByDay().isEmpty()) {
631+
DayOfWeek startDay = DayOfWeek.valueOf(startDate.getDayOfWeek().name().substring(0, 3));
632+
embeddable.setByDay(List.of(startDay));
633+
} else {
634+
embeddable.setByDay(request.getByDay());
635+
}
636+
}
637+
}
620638

621639
}

0 commit comments

Comments
 (0)