배경
recovery.py의 generate_recovery_options()는 복구안(RecoveryPlan/RecoveryPlanItem)을 계산·저장만 하고, 실제 DailyPlan/DailyPlanItem에는 반영하지 않는다(#37). 사용자가 두 복구안(분량유지형/핵심집중형) 중 하나를 선택했을 때 실제 일정에 적용하는 로직이 필요하다.
처리 흐름
사용자가 복구안 선택
→ 같은 recovery_group의 다른 복구안 포함 유효성 검사
→ RESCHEDULE 항목을 changed_date의 DailyPlan에 배치
→ EXCLUDE 항목은 일정에서 제외 (새로 생성 안 함)
→ 선택한 복구안 APPLIED 처리
→ 같은 recovery_group의 나머지 복구안 DISCARDED 처리
구현 대상
planner/services/recovery.py에 apply_recovery_plan(recovery_plan) 추가
필수 처리
- 같은
recovery_group_id의 모든 RecoveryPlan을 잠그고, 그룹 내 이미 APPLIED된 안이 있으면 거부 (분량유지형/핵심집중형 동시 적용 방지)
- 선택한 복구안이 PENDING이 아니면 거부
transaction.atomic() 적용
- 적용 직전 changed_date별 AvailableTime/기존 배치 시간/시험일을 재검증 — 문제 있으면
RecoveryPlanStaleError로 전체 롤백 (복구안 계산 이후 가용시간이 바뀌었을 수 있으므로)
- RESCHEDULE 항목은
changed_date의 DailyPlan에 새 DailyPlanItem 생성 (remaining_minutes를 planned_minutes로 사용)
- EXCLUDE 항목은 새 일정에 생성하지 않음
- 선택한 복구안은 APPLIED, 같은 그룹의 나머지 PENDING은 DISCARDED (RecoveryPlanStatus에 REJECTED 없음 — 기존 DISCARDED 사용)
RecoveryPlan에 recovery_group_id당 APPLIED 상태 1개만 허용하는 조건부 UniqueConstraint 추가
DB 변경
RecoveryPlan에 Meta.constraints로 UniqueConstraint(fields=['recovery_group_id'], condition=Q(status=APPLIED), name='unique_applied_recovery_plan_per_group') 추가
이번 PR 범위 밖
테스트
- RESCHEDULE 항목이 changed_date의 DailyPlan에 정상 생성되는지
- EXCLUDE 항목은 생성되지 않는지
- 선택한 안은 APPLIED, sibling 안은 DISCARDED로 바뀌는지
- 같은 복구안 중복 적용 시 거부
- 그룹 내 다른 안이 이미 적용된 상태에서 적용 시도 시 거부
- DISCARDED 상태 복구안 적용 시도 시 거부
- AvailableTime이 삭제된 상태에서 적용 시 RecoveryPlanStaleError + 롤백
- 가용 용량이 줄어든 상태에서 적용 시 RecoveryPlanStaleError + 롤백
배경
recovery.py의generate_recovery_options()는 복구안(RecoveryPlan/RecoveryPlanItem)을 계산·저장만 하고, 실제 DailyPlan/DailyPlanItem에는 반영하지 않는다(#37). 사용자가 두 복구안(분량유지형/핵심집중형) 중 하나를 선택했을 때 실제 일정에 적용하는 로직이 필요하다.처리 흐름
사용자가 복구안 선택
→ 같은 recovery_group의 다른 복구안 포함 유효성 검사
→ RESCHEDULE 항목을 changed_date의 DailyPlan에 배치
→ EXCLUDE 항목은 일정에서 제외 (새로 생성 안 함)
→ 선택한 복구안 APPLIED 처리
→ 같은 recovery_group의 나머지 복구안 DISCARDED 처리
구현 대상
planner/services/recovery.py에apply_recovery_plan(recovery_plan)추가필수 처리
recovery_group_id의 모든 RecoveryPlan을 잠그고, 그룹 내 이미 APPLIED된 안이 있으면 거부 (분량유지형/핵심집중형 동시 적용 방지)transaction.atomic()적용RecoveryPlanStaleError로 전체 롤백 (복구안 계산 이후 가용시간이 바뀌었을 수 있으므로)changed_date의 DailyPlan에 새 DailyPlanItem 생성 (remaining_minutes를planned_minutes로 사용)RecoveryPlan에recovery_group_id당 APPLIED 상태 1개만 허용하는 조건부 UniqueConstraint 추가DB 변경
RecoveryPlan에Meta.constraints로UniqueConstraint(fields=['recovery_group_id'], condition=Q(status=APPLIED), name='unique_applied_recovery_plan_per_group')추가이번 PR 범위 밖
테스트