Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
77b2ec3
feat: applcation-develop.yml에 quartz설정 추가
bwnfo3 Sep 29, 2025
d0dc7d4
feat: QuartzConfig
bwnfo3 Sep 29, 2025
e369052
feat: Quartz에 스케줄 동기화 추가
bwnfo3 Sep 29, 2025
dccde18
feat: ScheduleController
bwnfo3 Sep 29, 2025
3909a1a
feat: Schedule 단건조회, 스케줄 활성상태 변경 추가
bwnfo3 Sep 29, 2025
4a01cb0
feat: ScheduleService
bwnfo3 Sep 29, 2025
7a70c57
feat: ScheduleUpdateDto
bwnfo3 Sep 29, 2025
294c1ed
feat: workflowController delete,patch,활성화변경, 스케줄 삭제 추가
bwnfo3 Sep 29, 2025
fd16ab8
feat: workflowMapper 워크플로우 활성화상태 변경 추가
bwnfo3 Sep 29, 2025
05d4087
feat: Schedule Quartz 실시간 반영 관련 내용 추가
bwnfo3 Sep 29, 2025
b60c3c7
feat: ScheduleManagementE2eTest 작성중
bwnfo3 Sep 29, 2025
b55ceab
Merge branch 'develop' of github.com:Kernel180-BE12/Final-4team-iceba…
bwnfo3 Sep 29, 2025
79392f7
chore: spotlessApply
bwnfo3 Sep 29, 2025
3de6725
feat: schedule workflow_id unique 조건 제거
bwnfo3 Sep 29, 2025
71fba1f
fix: schedule 관련 파일들 schedule 폴더로 이동
bwnfo3 Sep 29, 2025
1d49e85
feat: scheduleE2eTest 수정
bwnfo3 Sep 29, 2025
a1c1aa7
fix: 중복 코드 삭제
bwnfo3 Sep 29, 2025
e4f2964
fix: 검증코드 workflowservice -> scheduleService로 이동
bwnfo3 Sep 29, 2025
33418ae
fix: 오타 수정
bwnfo3 Sep 29, 2025
26a2e9e
fix: 정적 메서드로 변경
bwnfo3 Sep 29, 2025
9a72ff4
fix: 정적 메서드로 변경
bwnfo3 Sep 29, 2025
839eb4d
fix: 인증 중복 코드 제거
bwnfo3 Sep 29, 2025
bcbb8f9
fix: hasSchedules collection으로 변경
bwnfo3 Sep 29, 2025
fb59ff3
chore: import 추가
bwnfo3 Sep 29, 2025
a5efe5a
chore: spotlessApply
bwnfo3 Sep 29, 2025
eac711e
Merge branch 'develop' into feature/schedule-quartz
jihukimme Oct 2, 2025
0eba2fc
refactor: DTO 네이밍 변경
jihukimme Oct 2, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
package site.icebang.domain.schedule.controller;

import java.util.List;

import org.springframework.http.HttpStatus;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;

import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import site.icebang.common.dto.ApiResponseDto;
import site.icebang.domain.auth.model.AuthCredential;
import site.icebang.domain.schedule.dto.ScheduleCreateDto;
import site.icebang.domain.schedule.dto.ScheduleUpdateDto;
import site.icebang.domain.schedule.model.Schedule;
import site.icebang.domain.schedule.service.ScheduleService;

/**
* 스케줄 관리를 위한 REST API 컨트롤러입니다.
*
* <p>스케줄의 조회, 수정, 삭제, 활성화/비활성화 API를 제공합니다.
*
* <h2>제공 API:</h2>
*
* <ul>
* <li>GET /v0/workflows/{workflowId}/schedules - 워크플로우의 스케줄 목록 조회
* <li>GET /v0/schedules/{scheduleId} - 스케줄 단건 조회
* <li>PUT /v0/schedules/{scheduleId} - 스케줄 수정
* <li>PATCH /v0/schedules/{scheduleId}/active - 스케줄 활성화/비활성화
* <li>DELETE /v0/schedules/{scheduleId} - 스케줄 삭제
* </ul>
*
* @author bwnfo0702@gmail.com
* @since v0.1.0
*/
@Slf4j
@RestController
@RequestMapping("/v0")
@RequiredArgsConstructor
public class ScheduleController {

private final ScheduleService scheduleService;

@PostMapping("/workflows/{workflowId}/schedules")
@ResponseStatus(HttpStatus.CREATED)
public ApiResponseDto<Schedule> createSchedule(
@PathVariable Long workflowId,
@Valid @RequestBody ScheduleCreateDto dto,
@AuthenticationPrincipal AuthCredential authCredential) {

Long userId = authCredential.getId().longValue();
Schedule schedule = scheduleService.createSchedule(workflowId, dto, userId);

return ApiResponseDto.success(schedule);
}

/**
* 특정 워크플로우의 모든 스케줄을 조회합니다.
*
* @param workflowId 워크플로우 ID
* @return 스케줄 목록
*/
@GetMapping("/workflows/{workflowId}/schedules")
public ApiResponseDto<List<Schedule>> getSchedulesByWorkflow(@PathVariable Long workflowId) {
log.info("워크플로우 스케줄 목록 조회 요청: Workflow ID {}", workflowId);
List<Schedule> schedules = scheduleService.getSchedulesByWorkflowId(workflowId);
return ApiResponseDto.success(schedules);
}

/**
* 스케줄 ID로 단건 조회합니다.
*
* @param scheduleId 스케줄 ID
* @return 스케줄 정보
*/
@GetMapping("/schedules/{scheduleId}")
public ApiResponseDto<Schedule> getSchedule(@PathVariable Long scheduleId) {
log.info("스케줄 조회 요청: Schedule ID {}", scheduleId);
Schedule schedule = scheduleService.getScheduleById(scheduleId);
return ApiResponseDto.success(schedule);
}

/**
* 스케줄을 수정합니다.
*
* <p>크론 표현식, 스케줄 텍스트, 활성화 상태를 수정할 수 있으며, 변경사항은 즉시 Quartz에 반영됩니다.
*
* @param scheduleId 수정할 스케줄 ID
* @param dto 수정 정보
* @param authCredential 인증 정보 (수정자)
* @return 성공 응답
*/
@PutMapping("/schedules/{scheduleId}")
public ApiResponseDto<Void> updateSchedule(
@PathVariable Long scheduleId,
@Valid @RequestBody ScheduleUpdateDto dto,
@AuthenticationPrincipal AuthCredential authCredential) {

log.info("스케줄 수정 요청: Schedule ID {} - {}", scheduleId, dto.getCronExpression());

// 인증 체크
if (authCredential == null) {
throw new IllegalArgumentException("로그인이 필요합니다");
}

Long userId = authCredential.getId().longValue();
scheduleService.updateSchedule(scheduleId, dto, userId);

return ApiResponseDto.success(null);
}

/**
* 스케줄 활성화 상태를 변경합니다.
*
* <p>활성화(true) 시 Quartz에 등록되어 실행되고, 비활성화(false) 시 Quartz에서 제거됩니다.
*
* @param scheduleId 스케줄 ID
* @param isActive 변경할 활성화 상태
* @return 성공 응답
*/
@PatchMapping("/schedules/{scheduleId}/active")
public ApiResponseDto<Void> toggleScheduleActive(
@PathVariable Long scheduleId, @RequestParam Boolean isActive) {

log.info("스케줄 활성화 상태 변경 요청: Schedule ID {} - {}", scheduleId, isActive);
scheduleService.toggleScheduleActive(scheduleId, isActive);

return ApiResponseDto.success(null);
}

/**
* 스케줄을 삭제합니다 (논리 삭제).
*
* <p>DB에서 비활성화되고 Quartz에서도 제거됩니다.
*
* @param scheduleId 삭제할 스케줄 ID
* @return 성공 응답
*/
@DeleteMapping("/schedules/{scheduleId}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public ApiResponseDto<Void> deleteSchedule(@PathVariable Long scheduleId) {
log.info("스케줄 삭제 요청: Schedule ID {}", scheduleId);
scheduleService.deleteSchedule(scheduleId);
return ApiResponseDto.success(null);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package site.icebang.domain.workflow.dto;
package site.icebang.domain.schedule.dto;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
Expand Down Expand Up @@ -85,17 +85,18 @@ public class ScheduleCreateDto {
*
* <p>DTO의 정보를 DB 저장용 엔티티로 변환하며, 서비스 레이어에서 주입되는 workflowId와 userId를 함께 설정합니다.
*
* @param dto 변환할 ScheduleCreateDto 객체
* @param workflowId 연결할 워크플로우 ID
* @param userId 생성자 ID
* @return DB 저장 가능한 Schedule 엔티티
*/
public Schedule toEntity(Long workflowId, Long userId) {
public static Schedule toEntity(ScheduleCreateDto dto, Long workflowId, Long userId) {
return Schedule.builder()
.workflowId(workflowId)
.cronExpression(this.cronExpression)
.scheduleText(this.scheduleText)
.isActive(this.isActive != null ? this.isActive : true)
.parameters(this.parameters)
.cronExpression(dto.cronExpression)
.scheduleText(dto.scheduleText)
.isActive(dto.isActive != null ? dto.isActive : true)
.parameters(dto.parameters)
.createdBy(userId)
.updatedBy(userId)
.build();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package site.icebang.domain.workflow.dto;
package site.icebang.domain.schedule.dto;

import java.time.Instant;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package site.icebang.domain.schedule.dto;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
* 스케줄 수정 요청을 위한 DTO 클래스입니다.
*
* <p>기존 스케줄의 크론 표현식, 스케줄 텍스트, 활성화 상태 등을 수정할 때 사용합니다.
*
* <h2>검증 규칙:</h2>
*
* <ul>
* <li>cronExpression: 필수값, Quartz 크론식 형식
* <li>scheduleText: 선택값, 사용자 친화적 스케줄 설명 (예: "매일 오전 8시")
* <li>isActive: 필수값, 스케줄 활성화 여부
* </ul>
*
* @author bwnfo0702@gmail.com
* @since v0.1.0
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ScheduleUpdateDto {

/**
* Quartz 크론 표현식
*
* <p>예시: "0 0 8 * * ?" (매일 오전 8시)
*/
@NotBlank(message = "크론 표현식은 필수입니다")
private String cronExpression;

/**
* 사용자 친화적 스케줄 설명 텍스트
*
* <p>예시: "매일 오전 8시", "매주 월요일 오후 6시"
*/
private String scheduleText;

/**
* 스케줄 활성화 여부
*
* <p>true: 활성화 (실행됨), false: 비활성화 (실행 안 됨)
*/
@NotNull(message = "활성화 상태는 필수입니다")
private Boolean isActive;
}
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,12 @@ Schedule findByWorkflowIdAndCronExpression(
* @return 영향받은 행 수
*/
int deactivateAllByWorkflowId(@Param("workflowId") Long workflowId);

/**
* 스케줄 ID로 단건 조회
*
* @param id 스케줄 ID
* @return 스케줄 정보, 없으면 null
*/
Schedule findById(@Param("id") Long id);
}
Loading
Loading