PlanCommandService.java
package com.deveagles.be15_deveagles_be.features.schedules.command.application.service;
import com.deveagles.be15_deveagles_be.common.exception.BusinessException;
import com.deveagles.be15_deveagles_be.common.exception.ErrorCode;
import com.deveagles.be15_deveagles_be.features.schedules.command.application.dto.request.CreatePlanRequest;
import com.deveagles.be15_deveagles_be.features.schedules.command.application.dto.request.CreateRegularPlanRequest;
import com.deveagles.be15_deveagles_be.features.schedules.command.application.dto.request.DeleteScheduleRequest;
import com.deveagles.be15_deveagles_be.features.schedules.command.application.dto.request.UpdatePlanScheduleRequest;
import com.deveagles.be15_deveagles_be.features.schedules.command.domain.aggregate.Plan;
import com.deveagles.be15_deveagles_be.features.schedules.command.domain.aggregate.RegularPlan;
import com.deveagles.be15_deveagles_be.features.schedules.command.domain.aggregate.ScheduleType;
import com.deveagles.be15_deveagles_be.features.schedules.command.domain.repository.PlanRepository;
import com.deveagles.be15_deveagles_be.features.schedules.command.domain.repository.RegularPlanRepository;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@RequiredArgsConstructor
public class PlanCommandService {
private final PlanRepository planRepository;
private final RegularPlanRepository regularPlanRepository;
public Long createPlan(Long shopId, CreatePlanRequest request) {
if (request.planStartAt().isAfter(request.planEndAt())) {
throw new BusinessException(ErrorCode.INVALID_RESERVATION_TIME_RANGE);
}
Plan plan =
Plan.builder()
.staffId(request.staffId())
.shopId(shopId)
.planTitle(request.planTitle())
.planMemo(request.planMemo())
.planStartAt(request.planStartAt())
.planEndAt(request.planEndAt())
.build();
return planRepository.save(plan).getPlanId();
}
public Long createRegularPlan(Long shopId, CreateRegularPlanRequest request) {
if (request.regularPlanStartAt().isAfter(request.regularPlanEndAt())) {
throw new BusinessException(ErrorCode.INVALID_RESERVATION_TIME_RANGE);
}
boolean hasWeekly = request.weeklyPlan() != null;
boolean hasMonthly = request.monthlyPlan() != null;
if (hasWeekly == hasMonthly) {
throw new BusinessException(ErrorCode.INVALID_SCHEDULE_REPEAT_TYPE);
}
RegularPlan regularPlan =
RegularPlan.builder()
.staffId(request.staffId())
.shopId(shopId)
.regularPlanTitle(request.regularPlanTitle())
.monthlyPlan(request.monthlyPlan())
.weeklyPlan(request.weeklyPlan())
.regularPlanMemo(request.regularPlanMemo())
.regularPlanStartAt(request.regularPlanStartAt())
.regularPlanEndAt(request.regularPlanEndAt())
.build();
return regularPlanRepository.save(regularPlan).getRegularPlanId();
}
// 일정 (단기, 정기) 다건 삭제
@Transactional
public void deleteMixedSchedules(Long shopId, List<DeleteScheduleRequest> requests) {
if (requests == null || requests.isEmpty()) return;
List<Long> planIds = new ArrayList<>();
List<Long> regularPlanIds = new ArrayList<>();
for (DeleteScheduleRequest req : requests) {
String type = req.type().toLowerCase(Locale.ROOT);
if ("plan".equals(type)) {
planIds.add(req.id());
} else if ("regular_plan".equals(type)) {
regularPlanIds.add(req.id());
} else {
throw new BusinessException(ErrorCode.INVALID_SCHEDULE_TYPE);
}
}
if (!planIds.isEmpty()) {
List<Plan> plans = planRepository.findAllById(planIds);
if (plans.size() != planIds.size()) {
throw new BusinessException(ErrorCode.PLAN_NOT_FOUND);
}
planRepository.deleteAllInBatch(plans);
}
if (!regularPlanIds.isEmpty()) {
List<RegularPlan> regularPlans = regularPlanRepository.findAllById(regularPlanIds);
if (regularPlans.size() != regularPlanIds.size()) {
throw new BusinessException(ErrorCode.REGULAR_PLAN_NOT_FOUND);
}
regularPlanRepository.deleteAllInBatch(regularPlans);
}
}
@Transactional
public void updatePlan(Long shopId, Long planId, CreatePlanRequest request) {
Plan plan =
planRepository
.findById(planId)
.orElseThrow(() -> new BusinessException(ErrorCode.PLAN_NOT_FOUND));
if (request.planStartAt().isAfter(request.planEndAt())) {
throw new BusinessException(ErrorCode.INVALID_RESERVATION_TIME_RANGE);
}
// 값 수정
plan.update(
request.planTitle(), request.planMemo(), request.planStartAt(), request.planEndAt());
}
@Transactional
public void updateRegularPlan(Long shopId, Long regularPlanId, CreateRegularPlanRequest request) {
RegularPlan regularPlan =
regularPlanRepository
.findById(regularPlanId)
.orElseThrow(() -> new BusinessException(ErrorCode.REGULAR_PLAN_NOT_FOUND));
if (request.regularPlanStartAt().isAfter(request.regularPlanEndAt())) {
throw new BusinessException(ErrorCode.INVALID_RESERVATION_TIME_RANGE);
}
boolean hasWeekly = request.weeklyPlan() != null;
boolean hasMonthly = request.monthlyPlan() != null;
if (hasWeekly == hasMonthly) {
throw new BusinessException(ErrorCode.INVALID_SCHEDULE_REPEAT_TYPE);
}
regularPlan.update(
request.regularPlanTitle(),
request.monthlyPlan(),
request.weeklyPlan(),
request.regularPlanMemo(),
request.regularPlanStartAt(),
request.regularPlanEndAt());
}
@Transactional
public void switchSchedule(Long shopId, UpdatePlanScheduleRequest request) {
ScheduleType fromType = request.fromType();
ScheduleType toType = request.toType();
// 타입이 같으면 수정
if (fromType == toType) {
switch (fromType) {
case PLAN -> updatePlan(shopId, request.fromId(), request.planRequest());
case REGULAR_PLAN ->
updateRegularPlan(shopId, request.fromId(), request.regularPlanRequest());
default -> throw new BusinessException(ErrorCode.INVALID_SCHEDULE_TYPE);
}
return;
}
// 타입이 다르면 기존 삭제 + 새로 등록
switch (fromType) {
case PLAN -> {
if (!planRepository.existsById(request.fromId())) {
throw new BusinessException(ErrorCode.PLAN_NOT_FOUND);
}
planRepository.deleteById(request.fromId());
}
case REGULAR_PLAN -> {
if (!regularPlanRepository.existsById(request.fromId())) {
throw new BusinessException(ErrorCode.REGULAR_PLAN_NOT_FOUND);
}
regularPlanRepository.deleteById(request.fromId());
}
default -> throw new BusinessException(ErrorCode.INVALID_SCHEDULE_TYPE);
}
switch (toType) {
case PLAN -> createPlan(shopId, request.planRequest());
case REGULAR_PLAN -> createRegularPlan(shopId, request.regularPlanRequest());
default -> throw new BusinessException(ErrorCode.INVALID_SCHEDULE_TYPE);
}
}
}