From b3cea94ac66e370c77ced46330674e1dbbcfc0e1 Mon Sep 17 00:00:00 2001 From: "skykim5538@gmail.com" Date: Thu, 4 Sep 2025 15:10:22 +0900 Subject: [PATCH 01/20] =?UTF-8?q?test:=20=EC=97=85push=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/softlabs/aicontents/scheduler/TestScheduler.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/springboot/src/main/java/com/softlabs/aicontents/scheduler/TestScheduler.java b/springboot/src/main/java/com/softlabs/aicontents/scheduler/TestScheduler.java index e4d4dd96..b6eedea4 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/scheduler/TestScheduler.java +++ b/springboot/src/main/java/com/softlabs/aicontents/scheduler/TestScheduler.java @@ -2,3 +2,5 @@ public class TestScheduler { } + +// test From fb485cd9b4a48126d7ba659d0eb888fd3ac22b52 Mon Sep 17 00:00:00 2001 From: "skykim5538@gmail.com" Date: Thu, 4 Sep 2025 15:52:23 +0900 Subject: [PATCH 02/20] =?UTF-8?q?git=20commit=20-m=20"feat:=20RDS=20?= =?UTF-8?q?=EC=97=B0=EA=B2=B0=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EB=B0=8F=20=EC=84=A4=EC=A0=95=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - RDS 연결 및 웹 출력 테스트 코드 - application.properties 파일 DB 관련 설정 추가 - build.gradle 파일 Oracle MyBatis 설정 추가 " --- springboot/build.gradle | 4 +++ .../orchestration/TestOrchestration.java | 4 --- .../orchestration/enums/PipelineStep.java | 6 ++++ .../aicontents/scheduler/TestScheduler.java | 6 ---- .../controller/SchedulerController.java | 34 +++++++++++++++++++ ...4\240\225 API \353\213\264\353\213\271.md" | 1 + .../scheduler/dto/ScheduleRequestDTO.java | 17 ++++++++++ .../scheduler/dto/ScheduleResponseDTO.java | 13 +++++++ .../scheduler/mapper/ScheduledTaskMapper.java | 17 ++++++++++ .../service/ScheduledTaskService.java | 20 +++++++++++ .../scheduler/vo/ScheduledRequestVO.java | 13 +++++++ .../scheduler/vo/ScheduledResponseVO.java | 16 +++++++++ .../src/main/resources/application.properties | 20 +++++++++++ .../mapper/scheduler/ScheduledTaskMapper.xml | 18 ++++++++++ 14 files changed, 179 insertions(+), 10 deletions(-) delete mode 100644 springboot/src/main/java/com/softlabs/aicontents/orchestration/TestOrchestration.java create mode 100644 springboot/src/main/java/com/softlabs/aicontents/orchestration/enums/PipelineStep.java delete mode 100644 springboot/src/main/java/com/softlabs/aicontents/scheduler/TestScheduler.java create mode 100644 springboot/src/main/java/com/softlabs/aicontents/scheduler/controller/SchedulerController.java create mode 100644 "springboot/src/main/java/com/softlabs/aicontents/scheduler/controller/\353\214\200\354\213\234\353\263\264\353\223\234_\354\212\244\354\274\200\354\244\204 \354\204\244\354\240\225 API \353\213\264\353\213\271.md" create mode 100644 springboot/src/main/java/com/softlabs/aicontents/scheduler/dto/ScheduleRequestDTO.java create mode 100644 springboot/src/main/java/com/softlabs/aicontents/scheduler/dto/ScheduleResponseDTO.java create mode 100644 springboot/src/main/java/com/softlabs/aicontents/scheduler/mapper/ScheduledTaskMapper.java create mode 100644 springboot/src/main/java/com/softlabs/aicontents/scheduler/service/ScheduledTaskService.java create mode 100644 springboot/src/main/java/com/softlabs/aicontents/scheduler/vo/ScheduledRequestVO.java create mode 100644 springboot/src/main/java/com/softlabs/aicontents/scheduler/vo/ScheduledResponseVO.java create mode 100644 springboot/src/main/resources/mapper/scheduler/ScheduledTaskMapper.xml diff --git a/springboot/build.gradle b/springboot/build.gradle index a6201b8e..863029bf 100644 --- a/springboot/build.gradle +++ b/springboot/build.gradle @@ -36,6 +36,10 @@ dependencies { testRuntimeOnly 'org.junit.platform:junit-platform-launcher' // Swagger implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.3.0' + + //Oracle + MyBatis 추가 (20250902) + implementation 'com.oracle.database.jdbc:ojdbc11' + implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:3.0.3' } tasks.named('test') { diff --git a/springboot/src/main/java/com/softlabs/aicontents/orchestration/TestOrchestration.java b/springboot/src/main/java/com/softlabs/aicontents/orchestration/TestOrchestration.java deleted file mode 100644 index 8b5d7d65..00000000 --- a/springboot/src/main/java/com/softlabs/aicontents/orchestration/TestOrchestration.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.softlabs.aicontents.orchestration; - -public class TestOrchestration { -} diff --git a/springboot/src/main/java/com/softlabs/aicontents/orchestration/enums/PipelineStep.java b/springboot/src/main/java/com/softlabs/aicontents/orchestration/enums/PipelineStep.java new file mode 100644 index 00000000..e9ec0c7b --- /dev/null +++ b/springboot/src/main/java/com/softlabs/aicontents/orchestration/enums/PipelineStep.java @@ -0,0 +1,6 @@ +package com.softlabs.aicontents.orchestration.enums; + +public class PipelineStep { + //파이프라인 단계 정의 + +} diff --git a/springboot/src/main/java/com/softlabs/aicontents/scheduler/TestScheduler.java b/springboot/src/main/java/com/softlabs/aicontents/scheduler/TestScheduler.java deleted file mode 100644 index b6eedea4..00000000 --- a/springboot/src/main/java/com/softlabs/aicontents/scheduler/TestScheduler.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.softlabs.aicontents.scheduler; - -public class TestScheduler { -} - -// test diff --git a/springboot/src/main/java/com/softlabs/aicontents/scheduler/controller/SchedulerController.java b/springboot/src/main/java/com/softlabs/aicontents/scheduler/controller/SchedulerController.java new file mode 100644 index 00000000..d03782ed --- /dev/null +++ b/springboot/src/main/java/com/softlabs/aicontents/scheduler/controller/SchedulerController.java @@ -0,0 +1,34 @@ +package com.softlabs.aicontents.scheduler.controller; + +import com.softlabs.aicontents.scheduler.service.ScheduledTaskService; + +import org.springframework.web.bind.annotation.*; + +@RestController +// REST API를 처리하는 컨트롤러이다. +@RequestMapping("/api") +//이 클래스의 모든 URL은 /api로 시작한다 +@CrossOrigin(origins = "*") +//모든 도메인(="*")에서 이 API 호출 허용 + +public class SchedulerController { + + private final ScheduledTaskService scheduledTaskService; + + public SchedulerController(ScheduledTaskService scheduledTaskService) { + this.scheduledTaskService = scheduledTaskService; + } + //scheduledTaskService 타입의 변수 선언 + + //더미 데이터 제거 -> 실제 DB 조회 + @GetMapping("/tasks") //스케줄 목록을 조회 + //GET 방식으로 /api/test URL 요청이 오면 이 메서드 실행 + public String testConnection(){ + //테스트 메서드 선언 + int count = scheduledTaskService.getTaskCount(); + //service의 getTaskCount() 메서드 호출해서 결과를 count 변수에 저장 + + return "RDS 연결 성공 TEST_SCHEDULED_TASKS 테이블에" + count + "개 조회됨"; + + } +} diff --git "a/springboot/src/main/java/com/softlabs/aicontents/scheduler/controller/\353\214\200\354\213\234\353\263\264\353\223\234_\354\212\244\354\274\200\354\244\204 \354\204\244\354\240\225 API \353\213\264\353\213\271.md" "b/springboot/src/main/java/com/softlabs/aicontents/scheduler/controller/\353\214\200\354\213\234\353\263\264\353\223\234_\354\212\244\354\274\200\354\244\204 \354\204\244\354\240\225 API \353\213\264\353\213\271.md" new file mode 100644 index 00000000..356e3cbb --- /dev/null +++ "b/springboot/src/main/java/com/softlabs/aicontents/scheduler/controller/\353\214\200\354\213\234\353\263\264\353\223\234_\354\212\244\354\274\200\354\244\204 \354\204\244\354\240\225 API \353\213\264\353\213\271.md" @@ -0,0 +1 @@ +대시보드 설정 값-> 스케줄 설정 관련 API 연결 기능 \ No newline at end of file diff --git a/springboot/src/main/java/com/softlabs/aicontents/scheduler/dto/ScheduleRequestDTO.java b/springboot/src/main/java/com/softlabs/aicontents/scheduler/dto/ScheduleRequestDTO.java new file mode 100644 index 00000000..12912c7e --- /dev/null +++ b/springboot/src/main/java/com/softlabs/aicontents/scheduler/dto/ScheduleRequestDTO.java @@ -0,0 +1,17 @@ +package com.softlabs.aicontents.scheduler.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public record ScheduleRequestDTO( + @JsonProperty("taskId") Long taskId, + @JsonProperty("taskName") String taskName, + @JsonProperty("taskDescription") String taskDescription, + @JsonProperty("cronExpression") String cronExpression, + @JsonProperty("taskType") String taskType, + @JsonProperty("isActive") Boolean isActive, + @JsonProperty("nextExecution") String nextExecution, + @JsonProperty("lastExecution") String lastExecution, + @JsonProperty("status") String status +) { + +} \ No newline at end of file diff --git a/springboot/src/main/java/com/softlabs/aicontents/scheduler/dto/ScheduleResponseDTO.java b/springboot/src/main/java/com/softlabs/aicontents/scheduler/dto/ScheduleResponseDTO.java new file mode 100644 index 00000000..b79a0072 --- /dev/null +++ b/springboot/src/main/java/com/softlabs/aicontents/scheduler/dto/ScheduleResponseDTO.java @@ -0,0 +1,13 @@ +package com.softlabs.aicontents.scheduler.dto; + +public record ScheduleResponseDTO( + Long taskId, + String taskName, + String taskDescription, + String cronExpression, + String taskType, + boolean isActive, + String nextExecution, + String lastExecution, + String status +) {} \ No newline at end of file diff --git a/springboot/src/main/java/com/softlabs/aicontents/scheduler/mapper/ScheduledTaskMapper.java b/springboot/src/main/java/com/softlabs/aicontents/scheduler/mapper/ScheduledTaskMapper.java new file mode 100644 index 00000000..b53ad923 --- /dev/null +++ b/springboot/src/main/java/com/softlabs/aicontents/scheduler/mapper/ScheduledTaskMapper.java @@ -0,0 +1,17 @@ +package com.softlabs.aicontents.scheduler.mapper; + + +import org.apache.ibatis.annotations.Mapper; +import org.springframework.scheduling.config.ScheduledTask; + +import java.util.List; + +@Mapper +//이 인터페이스는 MyBatis가 관리하는 Mapper다 +public interface ScheduledTaskMapper { + //인터페이스 시작 + + int countTasks(); + //countTasks라는 메서드 선언, 반환 int + // 실제 구현은 xml에 있음. +} \ No newline at end of file diff --git a/springboot/src/main/java/com/softlabs/aicontents/scheduler/service/ScheduledTaskService.java b/springboot/src/main/java/com/softlabs/aicontents/scheduler/service/ScheduledTaskService.java new file mode 100644 index 00000000..52532f26 --- /dev/null +++ b/springboot/src/main/java/com/softlabs/aicontents/scheduler/service/ScheduledTaskService.java @@ -0,0 +1,20 @@ +package com.softlabs.aicontents.scheduler.service; + + +import com.softlabs.aicontents.scheduler.mapper.ScheduledTaskMapper; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +//Lombok 어노테이션 +public class ScheduledTaskService { + + private final ScheduledTaskMapper scheduledTaskMapper; + + public int getTaskCount() { + //반환은 int , 메서드 선언 + int res = scheduledTaskMapper.countTasks(); + return res; + } +} diff --git a/springboot/src/main/java/com/softlabs/aicontents/scheduler/vo/ScheduledRequestVO.java b/springboot/src/main/java/com/softlabs/aicontents/scheduler/vo/ScheduledRequestVO.java new file mode 100644 index 00000000..ae85c06b --- /dev/null +++ b/springboot/src/main/java/com/softlabs/aicontents/scheduler/vo/ScheduledRequestVO.java @@ -0,0 +1,13 @@ +package com.softlabs.aicontents.scheduler.vo; + +public record ScheduledRequestVO( + String taskName, + String taskDescription, + String cronExpression, + String taskType, + String isActive, + Integer maxRetryCount, + Integer timeoutMinutes + // ID, 생성일시 등 DB 자동생성 필드 제외 +) {} + diff --git a/springboot/src/main/java/com/softlabs/aicontents/scheduler/vo/ScheduledResponseVO.java b/springboot/src/main/java/com/softlabs/aicontents/scheduler/vo/ScheduledResponseVO.java new file mode 100644 index 00000000..a5240bc4 --- /dev/null +++ b/springboot/src/main/java/com/softlabs/aicontents/scheduler/vo/ScheduledResponseVO.java @@ -0,0 +1,16 @@ +package com.softlabs.aicontents.scheduler.vo; + +import java.time.LocalDateTime; + +public record ScheduledResponseVO( + Long taskId, + String taskName, + String taskDescription, + String cronExpression, + String taskType, + String isActive, + LocalDateTime createdAt, + LocalDateTime updatedAt + // DB에서 조회한 모든 필드 포함 + + ) {} \ No newline at end of file diff --git a/springboot/src/main/resources/application.properties b/springboot/src/main/resources/application.properties index ae64c683..e528a6db 100644 --- a/springboot/src/main/resources/application.properties +++ b/springboot/src/main/resources/application.properties @@ -1 +1,21 @@ spring.application.name=springboot + +# AWS - Oracle RDS +spring.datasource.url=jdbc:oracle:thin:@//final-7team-softlabs.cbqos8qeucft.ap-northeast-2.rds.amazonaws.com:1521/ORCL +spring.datasource.username=admin +spring.datasource.password=53775329 +spring.datasource.driver-class-name=oracle.jdbc.OracleDriver + +# RDS (20250902) +spring.datasource.hikari.maximum-pool-size=5 +spring.datasource.hikari.minimum-idle=2 +spring.datasource.hikari.connection-timeout=30000 +spring.datasource.hikari.idle-timeout=600000 + +# MyBatis (20250902) +mybatis.mapper-locations=classpath:mapper/**/*.xml +mybatis.type-aliases-package=com.softlabs.aicontents +mybatis.configuration.map-underscore-to-camel-case=true + +logging.level.org.mybatis=DEBUG +logging.level.com.softlabs.aicontents=DEBUG \ No newline at end of file diff --git a/springboot/src/main/resources/mapper/scheduler/ScheduledTaskMapper.xml b/springboot/src/main/resources/mapper/scheduler/ScheduledTaskMapper.xml new file mode 100644 index 00000000..d2f80c23 --- /dev/null +++ b/springboot/src/main/resources/mapper/scheduler/ScheduledTaskMapper.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + From 9dc5c1c0ce8f6d5f6e511a23eadff5b0ea2f267d Mon Sep 17 00:00:00 2001 From: "skykim5538@gmail.com" Date: Thu, 4 Sep 2025 22:04:00 +0900 Subject: [PATCH 03/20] =?UTF-8?q?feat:=20=ED=8C=8C=EC=9D=B4=ED=94=84?= =?UTF-8?q?=EB=9D=BC=EC=9D=B8=20=EC=97=94=EC=A7=84=20=EA=B8=B0=EB=B3=B8=20?= =?UTF-8?q?=EA=B5=AC=EC=A1=B0=20=EA=B5=AC=EC=B6=95=20(=EC=A7=84=ED=96=89?= =?UTF-8?q?=EC=A4=91)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - PIPELINE_EXECUTIONS 관리 시스템 기본 골조 - VO/DTO 분리 구조 및 Record 활용 - 실행 추적 및 로깅 시스템 설계 - 진행 중: MyBatis XML 작성 및 DB 연동 테스트 예정 --- .../scheduler/ContentAutomationScheduler.java | 10 +++ .../vo/PipelineExecutionRequestVO.java | 62 +++++++++++++++++++ .../vo/PipelineExecutionResponseVO.java | 33 ++++++++++ 3 files changed, 105 insertions(+) create mode 100644 springboot/src/main/java/com/softlabs/aicontents/scheduler/ContentAutomationScheduler.java create mode 100644 springboot/src/main/java/com/softlabs/aicontents/scheduler/vo/PipelineExecutionRequestVO.java create mode 100644 springboot/src/main/java/com/softlabs/aicontents/scheduler/vo/PipelineExecutionResponseVO.java diff --git a/springboot/src/main/java/com/softlabs/aicontents/scheduler/ContentAutomationScheduler.java b/springboot/src/main/java/com/softlabs/aicontents/scheduler/ContentAutomationScheduler.java new file mode 100644 index 00000000..4bb31725 --- /dev/null +++ b/springboot/src/main/java/com/softlabs/aicontents/scheduler/ContentAutomationScheduler.java @@ -0,0 +1,10 @@ +package com.softlabs.aicontents.scheduler; + + +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.stereotype.Component; + +@EnableScheduling +@Component +public class ContentAutomationScheduler { +} diff --git a/springboot/src/main/java/com/softlabs/aicontents/scheduler/vo/PipelineExecutionRequestVO.java b/springboot/src/main/java/com/softlabs/aicontents/scheduler/vo/PipelineExecutionRequestVO.java new file mode 100644 index 00000000..f019d97a --- /dev/null +++ b/springboot/src/main/java/com/softlabs/aicontents/scheduler/vo/PipelineExecutionRequestVO.java @@ -0,0 +1,62 @@ +package com.softlabs.aicontents.scheduler.vo; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + + +//자바 - DB 데이터 송신 객체 + +public record PipelineExecutionRequestVO( + + Long executionId, + Long taskId, + String executionStatus, + String executionMode, + String currentStepCode, + Integer totalSteps, + Integer completedSteps, + BigDecimal progressPercentage, + LocalDateTime scheduledTime, + LocalDateTime startTime, + LocalDateTime endTime, + Long durationMs, + Integer retryCount, + Integer maxRetries, + String errorStep, + String errorMessage, + String errorCode, + String executionResult, + String performanceMetrics +) { + // 새 실행 생성용 (정적)팩토리 메서드 패턴 + // 각 칼럼들의 타입을 미리 작성해 두어 새로 생성할 때 오류 방지 + public static PipelineExecutionRequestVO createNew(Long taskId,String executionMode){ + //메서드 이름은 creatNew + return new PipelineExecutionRequestVO( + null, // executionId (DB 자동 생성) + taskId, + "PENDING", + executionMode, + "STEP_01", + 4, + 0, + BigDecimal.ZERO, + LocalDateTime.now(), + null, + null, + null, + 0, + 3, + null, + null, + null, + null, + null + ); + + + } + + +} + diff --git a/springboot/src/main/java/com/softlabs/aicontents/scheduler/vo/PipelineExecutionResponseVO.java b/springboot/src/main/java/com/softlabs/aicontents/scheduler/vo/PipelineExecutionResponseVO.java new file mode 100644 index 00000000..c9214604 --- /dev/null +++ b/springboot/src/main/java/com/softlabs/aicontents/scheduler/vo/PipelineExecutionResponseVO.java @@ -0,0 +1,33 @@ +package com.softlabs.aicontents.scheduler.vo; + + +//PIPELINE_EXECUTIONS 테이블 조회용 + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +public record PipelineExecutionResponseVO( + + Long executionId, + Long taskId, + String executionStatus, // PENDING, RUNNING, COMPLETED, FAILED + String executionMode, // SCHEDULED, MANUAL + String currentStepCode, // STEP_01, STEP_02, STEP_03, STEP_04 + Integer totalSteps, + Integer completedSteps, + BigDecimal progressPercentage, + LocalDateTime scheduledTime, + LocalDateTime startTime, + LocalDateTime endTime, + Long durationMs, + Integer retryCount, + Integer maxRetries, + String errorStep, + String errorMessage, + String errorCode, + String executionResult, + String performanceMetrics + + +) { +} From 3f8ee14629f864f7b1497bd0d4553e5df11dfb74 Mon Sep 17 00:00:00 2001 From: "skykim5538@gmail.com" Date: Fri, 5 Sep 2025 17:44:25 +0900 Subject: [PATCH 04/20] =?UTF-8?q?feat:=20=ED=8C=8C=EC=9D=B4=ED=94=84?= =?UTF-8?q?=EB=9D=BC=EC=9D=B8=20=EB=BC=88=EB=8C=80=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 4단계 파이프라인 실행 엔진 및 컨텍스트 기반 상태 관리 추가 --- .../controller/ExecutionContext.java | 102 +++++++++ .../controller/PipelineOrchestrator.java | 204 ++++++++++++++++++ .../scheduler/mapper/SchedulerMapper.java | 21 ++ ...TaskService.java => SchedulerService.java} | 12 +- .../scheduler/ScheduledTaskMapper.xml | 17 +- 5 files changed, 352 insertions(+), 4 deletions(-) create mode 100644 springboot/src/main/java/com/softlabs/aicontents/scheduler/controller/ExecutionContext.java create mode 100644 springboot/src/main/java/com/softlabs/aicontents/scheduler/controller/PipelineOrchestrator.java create mode 100644 springboot/src/main/java/com/softlabs/aicontents/scheduler/mapper/SchedulerMapper.java rename springboot/src/main/java/com/softlabs/aicontents/scheduler/service/{ScheduledTaskService.java => SchedulerService.java} (51%) rename springboot/src/main/resources/{mapper => mappers}/scheduler/ScheduledTaskMapper.xml (50%) diff --git a/springboot/src/main/java/com/softlabs/aicontents/scheduler/controller/ExecutionContext.java b/springboot/src/main/java/com/softlabs/aicontents/scheduler/controller/ExecutionContext.java new file mode 100644 index 00000000..116b1dd7 --- /dev/null +++ b/springboot/src/main/java/com/softlabs/aicontents/scheduler/controller/ExecutionContext.java @@ -0,0 +1,102 @@ +package com.softlabs.aicontents.scheduler.controller; + +import com.softlabs.aicontents.scheduler.mapper.SchedulerMapper; + +public class ExecutionContext { + + // 실행 식별자 + private Long executionId; + private Long taskId; + private String currentStepName; + + // DB 접근용 + private SchedulerMapper scheduledMapper; + + // 생성자 + public ExecutionContext(SchedulerMapper scheduledMapper) { + this.scheduledMapper = scheduledMapper; + } + + /** + * 실행 시작시 호출 - DB에 실행 기록 생성 + */ + public void initialize(Long taskId) { + this.taskId = taskId; + + // TODO: PIPELINE_EXECUTIONS 테이블에 새 실행 기록 생성 + // TODO: executionId 설정 + + System.out.println("ExecutionContext 초기화 완료 - TaskID: " + taskId); + } + + /** + * 스케줄 작업 설정값 조회 + */ + public String getTaskSetting(String settingKey) { + // TODO: SCHEDULED_TASKS 테이블에서 설정 조회 + + switch(settingKey) { + case "target_keywords": + return "기본키워드1,기본키워드2"; // 임시 하드코딩 + case "pipeline_config": + return "{}"; // 임시 빈 JSON + default: + return null; + } + } + + /** + * 이전 단계 결과 조회 + */ + public String getPreviousStepResult(String stepName) { + // TODO: STEP_RESULTS 테이블에서 해당 단계의 output_data 조회 + + System.out.println("이전 단계 결과 조회: " + stepName); + return "임시_이전단계_결과_데이터"; // 임시 하드코딩 + } + + /** + * 현재 단계 결과 저장 + */ + public void saveCurrentStepResult(String stepName, String result) { + this.currentStepName = stepName; + + // TODO: STEP_RESULTS 테이블에 결과 저장 + // TODO: PIPELINE_EXECUTIONS 상태 업데이트 + + System.out.println("단계 결과 저장 - " + stepName + ": " + result.substring(0, Math.min(50, result.length()))); + } + + /** + * 실행 완료 처리 + */ + public void markCompleted() { + // TODO: PIPELINE_EXECUTIONS 상태를 'COMPLETED'로 변경 + // TODO: end_time 설정 + + System.out.println("실행 완료 처리 - ExecutionID: " + executionId); + } + + /** + * 실행 실패 처리 + */ + public void markFailed(String errorMessage) { + // TODO: PIPELINE_EXECUTIONS 상태를 'FAILED'로 변경 + // TODO: error_message 저장 + + System.out.println("실행 실패 처리 - " + errorMessage); + } + + // Getters + public Long getExecutionId() { + return executionId; + } + + public Long getTaskId() { + return taskId; + } + + public String getCurrentStepName() { + return currentStepName; + } +} \ No newline at end of file diff --git a/springboot/src/main/java/com/softlabs/aicontents/scheduler/controller/PipelineOrchestrator.java b/springboot/src/main/java/com/softlabs/aicontents/scheduler/controller/PipelineOrchestrator.java new file mode 100644 index 00000000..2fc97455 --- /dev/null +++ b/springboot/src/main/java/com/softlabs/aicontents/scheduler/controller/PipelineOrchestrator.java @@ -0,0 +1,204 @@ +package com.softlabs.aicontents.scheduler.controller; + + +import com.softlabs.aicontents.scheduler.mapper.SchedulerMapper; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.apache.coyote.http11.filters.SavedRequestInputFilter; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@Service +@Slf4j +public class PipelineOrchestrator { + + private final SchedulerMapper schedulerMapper; + + public PipelineOrchestrator(SchedulerMapper schedulerMapper) { + this.schedulerMapper = schedulerMapper; + + } + + + public void executePipeline(Long taskId) { + ExecutionContext context = new ExecutionContext(schedulerMapper); + + try{ + log.info("파이프라인 실행 시작 - TaskID: {}", taskId); + + //컨택스트 초기화 + context.initialize(taskId); + + //1단계 : 트랜드 크롤링 확인 + String step1Result= executeStep1(context); + context.saveCurrentStepResult("TREND_CRAWLING", step1Result); + + //2단계 : 컨탠츠 크롤링 + String step2Result = executeStep2(context); + context.saveCurrentStepResult("SSADAGU_CRAWLING", step2Result); + + //3단계 : AI 컨탠츠 생성 + String step3Result = executeStep3(context); + context.saveCurrentStepResult("AI_GENERATION", step3Result); + + // 4단계: 블로그 발행 + String step4Result = executeStep4(context); + context.saveCurrentStepResult("BLOG_PUBLISH", step4Result); + + // 완료 처리 + context.markCompleted(); + log.info("파이프라인 실행 완료 - TaskID: {}", taskId); + + } catch(Exception e) { + log.error("파이프라인 실행실패 - TaskID: {}, 오류: {}", taskId,e.getMessage(), e); + context.markFailed(e.getMessage()); + } + } + + // 메서드 + + //1단계 + private String executeStep1(ExecutionContext context){ + log.info("1단계 시작 : 트랜드 크롤링"); + + try{ + //설정 조회 + String keywords = context.getTaskSetting("target_keywords"); + log.info("크롤링 키워드 : {}",keywords); + + // TODO: 실제 TrendService 호출 + // TrendService trendService = new TrendService(); + // List results = trendService.crawl(Arrays.asList(keywords.split(","))); + + // 임시 Mock 데이터 + String mockResult = "[\"" + keywords + " 트렌드1\", \"" + keywords + " 트렌드2\", \"최신 이슈\"]"; + + log.info("1단계 완료: 트렌드 데이터 수집됨"); + return mockResult; + + } catch(Exception e){ + log.error("1단계 실패:{}",e.getMessage()); + throw new RuntimeException("트랜드 크롤링 실해:" +e.getMessage()); + } + + + } + /** + * 2단계: 콘텐츠 크롤링 + */ + private String executeStep2(ExecutionContext context) { + log.info("2단계 시작: 콘텐츠 크롤링"); + + try { + // 이전 단계 결과 조회 + String step1Data = context.getPreviousStepResult("TREND_CRAWLING"); + log.info("1단계 결과 활용: {}", step1Data); + + // TODO: 실제 SsadaguService 호출 + // SsadaguService ssadaguService = new SsadaguService(); + // List
results = ssadaguService.crawl(parseKeywords(step1Data)); + + // 임시 Mock 데이터 + String mockResult = "[{\"title\":\"관련 기사1\", \"content\":\"내용1\"}, {\"title\":\"관련 기사2\", \"content\":\"내용2\"}]"; + + log.info("2단계 완료: 컨텐츠 크롤링 완료"); + return mockResult; + + } catch (Exception e) { + log.error("2단계 실패: {}", e.getMessage()); + throw new RuntimeException("콘텐츠 크롤링 실패: " + e.getMessage()); + } + } + + /** + * 3단계: AI 콘텐츠 생성 + */ + private String executeStep3(ExecutionContext context) { + log.info("3단계 시작: AI 콘텐츠 생성"); + + try { + // 이전 단계들 결과 조회 + String step1Data = context.getPreviousStepResult("TREND_CRAWLING"); + String step2Data = context.getPreviousStepResult("SSADAGU_CRAWLING"); + + log.info("1,2단계 결과 활용 - 트렌드: {}, 크롤링: {}", + step1Data.substring(0, Math.min(50, step1Data.length())), + step2Data.substring(0, Math.min(50, step2Data.length()))); + + // TODO: 실제 AiService 호출 + // AiService aiService = new AiService(); + // String result = aiService.generateContent(step1Data, step2Data); + + // 임시 Mock 데이터 + String mockResult = "{\"title\":\"AI가 생성한 제목\", \"content\":\"AI가 생성한 블로그 내용...\", \"tags\":[\"태그1\", \"태그2\"]}"; + + log.info("3단계 완료: AI 콘텐츠 생성 완료"); + return mockResult; + + }catch (Exception e){ + log.error("3단계 실패: {} ",e.getMessage()); + throw new RuntimeException("AI 콘텐츠 생성 실패: " + e.getMessage()); + } + } + + /** + * 4단계: 블로그 발행 + */ + private String executeStep4(ExecutionContext context) { + log.info("4단계 시작: 블로그 발행"); + + try { + // 이전 단계 결과 조회 + String step3Data = context.getPreviousStepResult("AI_GENERATION"); + log.info("3단계 결과 활용: {}", step3Data.substring(0, Math.min(50, step3Data.length()))); + + // TODO: 실제 BlogService 호출 + // BlogService blogService = new BlogService(); + // String result = blogService.publish(step3Data); + + // 임시 Mock 데이터 + String mockResult = "{\"blog_url\":\"https://blog.example.com/post123\", \"post_id\":\"123\", \"status\":\"published\"}"; + + log.info("4단계 완료: 블로그 발행 완료"); + return mockResult; + + } catch (Exception e) { + log.error("4단계 실패: {} ", e.getMessage()); + throw new RuntimeException("블로그 발행 실패: " + e.getMessage()); + } + } + + /** + * 테스트용 컨트롤러 + */ + @RestController + @RequestMapping("/api/test") + public class PipelineTestController { + + private final PipelineOrchestrator pipelineOrchestrator; + + public PipelineTestController(PipelineOrchestrator pipelineOrchestrator) { + this.pipelineOrchestrator = pipelineOrchestrator; + } + + /** + * 파이프라인 수동 실행 테스트 + */ + @PostMapping("/execute/{taskId}") + public ResponseEntity testPipelineExecution(@PathVariable Long taskId) { + try { + pipelineOrchestrator.executePipeline(taskId); + return ResponseEntity.ok("파이프라인 실행 완료"); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) + .body("파이프라인 실행 실패: " + e.getMessage()); + } + } + } + + } diff --git a/springboot/src/main/java/com/softlabs/aicontents/scheduler/mapper/SchedulerMapper.java b/springboot/src/main/java/com/softlabs/aicontents/scheduler/mapper/SchedulerMapper.java new file mode 100644 index 00000000..5549a606 --- /dev/null +++ b/springboot/src/main/java/com/softlabs/aicontents/scheduler/mapper/SchedulerMapper.java @@ -0,0 +1,21 @@ +package com.softlabs.aicontents.scheduler.mapper; + + +import com.softlabs.aicontents.scheduler.vo.ScheduledResponseVO; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +@Mapper +//이 인터페이스는 MyBatis가 관리하는 Mapper다 +public interface SchedulerMapper { + //인터페이스 시작 + + int countTasks(); + //countTasks라는 메서드 선언, 반환 int + // 실제 구현은 xml에 있음. + + ScheduledResponseVO selectByTaskId(@Param("taskId") Long taskId); + //파라미터 이름 지정: XML에서 #{taskId}로 사용할 수 있게 이름을 지정 + //입력 파라미터: 이 메서드가 받는 데이터 + +} \ No newline at end of file diff --git a/springboot/src/main/java/com/softlabs/aicontents/scheduler/service/ScheduledTaskService.java b/springboot/src/main/java/com/softlabs/aicontents/scheduler/service/SchedulerService.java similarity index 51% rename from springboot/src/main/java/com/softlabs/aicontents/scheduler/service/ScheduledTaskService.java rename to springboot/src/main/java/com/softlabs/aicontents/scheduler/service/SchedulerService.java index 52532f26..f9cae313 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/scheduler/service/ScheduledTaskService.java +++ b/springboot/src/main/java/com/softlabs/aicontents/scheduler/service/SchedulerService.java @@ -1,20 +1,26 @@ package com.softlabs.aicontents.scheduler.service; -import com.softlabs.aicontents.scheduler.mapper.ScheduledTaskMapper; +import com.softlabs.aicontents.scheduler.mapper.SchedulerMapper; +import com.softlabs.aicontents.scheduler.vo.ScheduledResponseVO; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @Service @RequiredArgsConstructor //Lombok 어노테이션 -public class ScheduledTaskService { +public class SchedulerService { - private final ScheduledTaskMapper scheduledTaskMapper; + private final SchedulerMapper scheduledTaskMapper; public int getTaskCount() { //반환은 int , 메서드 선언 int res = scheduledTaskMapper.countTasks(); return res; } + public ScheduledResponseVO getTask(Long taskId) { + return scheduledTaskMapper.selectByTaskId(taskId); + } + + } diff --git a/springboot/src/main/resources/mapper/scheduler/ScheduledTaskMapper.xml b/springboot/src/main/resources/mappers/scheduler/ScheduledTaskMapper.xml similarity index 50% rename from springboot/src/main/resources/mapper/scheduler/ScheduledTaskMapper.xml rename to springboot/src/main/resources/mappers/scheduler/ScheduledTaskMapper.xml index d2f80c23..fb51960e 100644 --- a/springboot/src/main/resources/mapper/scheduler/ScheduledTaskMapper.xml +++ b/springboot/src/main/resources/mappers/scheduler/ScheduledTaskMapper.xml @@ -5,7 +5,7 @@ "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> - + @@ -14,5 +14,20 @@ FROM ADMIN.TEST_SCHEDULED_TASKS + + + + From 268da0cb8a3749d8dde6354d6fd63e15d08516a5 Mon Sep 17 00:00:00 2001 From: "skykim5538@gmail.com" Date: Tue, 9 Sep 2025 17:44:54 +0900 Subject: [PATCH 05/20] =?UTF-8?q?refactor:=20=EC=A0=84=EC=B2=B4=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=B4=ED=94=84=EB=9D=BC=EC=9D=B8=20=EC=84=9C?= =?UTF-8?q?=EB=B9=84=EC=8A=A4=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EA=B5=AC?= =?UTF-8?q?=EC=A1=B0=20=EA=B0=9C=ED=8E=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../f_001/controller/TestController.java | 8 +- .../domain/f_001/dto/request/TestReqDto.java | 8 +- .../domain/f_001/dto/response/TestResDto.java | 8 +- .../domain/f_001/mapper/TestMapper.java | 8 +- .../domain/f_001/service/TestService.java | 8 +- .../domain/f_001/vo/request/TestReqVo.java | 8 +- .../domain/f_001/vo/response/TestResVo.java | 8 +- .../domain/testMapper/AIContentMapper.java | 12 ++ .../domain/testMapper/BlogPublishMapper.java | 12 ++ .../domain/testMapper/KeywordMapper.java | 11 + .../testMapper/ProductCrawlingMapper.java | 13 ++ .../domain/testService/AIContentService.java | 14 ++ .../testService/BlogPublishService.java | 14 ++ .../domain/testService/KeywordService.java | 13 ++ .../testService/ProductCrawlingService.java | 12 ++ .../controller/TestExampleController.java | 56 ++--- .../testexample/entity/TestExample.java | 56 ++--- .../testexample/mapper/TestExampleMapper.java | 18 +- .../service/TestExampleService.java | 40 ++-- .../aicontents/health/HealthController.java | 70 +++--- .../aicontents/health/HealthMapper.java | 16 +- .../aicontents/health/HealthService.java | 82 +++---- .../com/softlabs/aicontents/log/TestLog.java | 8 +- .../orchestration/enums/PipelineStep.java | 18 +- .../scheduler/ContentAutomationScheduler.java | 10 - .../controller/ExecutionContext.java | 102 --------- .../controller/PipelineOrchestrator.java | 204 ------------------ .../scheduler/controller/PipelineService.java | 83 +++++++ .../controller/SchedulerController.java | 34 --- .../scheduler/dto/ScheduleRequestDTO.java | 17 -- .../scheduler/dto/ScheduleResponseDTO.java | 13 -- .../pipeLineDTO/StepExecutionResultDTO.java | 30 +++ .../interfacePipe/PipelineStepExecutor.java | 11 + .../scheduler/mapper/ScheduledTaskMapper.java | 17 -- .../scheduler/mapper/SchedulerMapper.java | 21 -- .../scheduler/service/AIContentExecutor.java | 68 ++++++ .../service/BlogPublishExecutor.java | 68 ++++++ .../scheduler/service/KeywordExecutor.java | 64 ++++++ .../service/ProductCrawlingExecutor.java | 68 ++++++ .../scheduler/service/SchedulerService.java | 26 --- .../vo/PipelineExecutionRequestVO.java | 62 ------ .../vo/PipelineExecutionResponseVO.java | 33 --- .../scheduler/vo/ScheduledRequestVO.java | 13 -- .../scheduler/vo/ScheduledResponseVO.java | 16 -- .../mappers/domain/example/HealthMapper.xml | 20 +- 45 files changed, 720 insertions(+), 781 deletions(-) create mode 100644 springboot/src/main/java/com/softlabs/aicontents/domain/testMapper/AIContentMapper.java create mode 100644 springboot/src/main/java/com/softlabs/aicontents/domain/testMapper/BlogPublishMapper.java create mode 100644 springboot/src/main/java/com/softlabs/aicontents/domain/testMapper/KeywordMapper.java create mode 100644 springboot/src/main/java/com/softlabs/aicontents/domain/testMapper/ProductCrawlingMapper.java create mode 100644 springboot/src/main/java/com/softlabs/aicontents/domain/testService/AIContentService.java create mode 100644 springboot/src/main/java/com/softlabs/aicontents/domain/testService/BlogPublishService.java create mode 100644 springboot/src/main/java/com/softlabs/aicontents/domain/testService/KeywordService.java create mode 100644 springboot/src/main/java/com/softlabs/aicontents/domain/testService/ProductCrawlingService.java delete mode 100644 springboot/src/main/java/com/softlabs/aicontents/scheduler/ContentAutomationScheduler.java delete mode 100644 springboot/src/main/java/com/softlabs/aicontents/scheduler/controller/ExecutionContext.java delete mode 100644 springboot/src/main/java/com/softlabs/aicontents/scheduler/controller/PipelineOrchestrator.java create mode 100644 springboot/src/main/java/com/softlabs/aicontents/scheduler/controller/PipelineService.java delete mode 100644 springboot/src/main/java/com/softlabs/aicontents/scheduler/controller/SchedulerController.java delete mode 100644 springboot/src/main/java/com/softlabs/aicontents/scheduler/dto/ScheduleRequestDTO.java delete mode 100644 springboot/src/main/java/com/softlabs/aicontents/scheduler/dto/ScheduleResponseDTO.java create mode 100644 springboot/src/main/java/com/softlabs/aicontents/scheduler/dto/pipeLineDTO/StepExecutionResultDTO.java create mode 100644 springboot/src/main/java/com/softlabs/aicontents/scheduler/interfacePipe/PipelineStepExecutor.java delete mode 100644 springboot/src/main/java/com/softlabs/aicontents/scheduler/mapper/ScheduledTaskMapper.java delete mode 100644 springboot/src/main/java/com/softlabs/aicontents/scheduler/mapper/SchedulerMapper.java create mode 100644 springboot/src/main/java/com/softlabs/aicontents/scheduler/service/AIContentExecutor.java create mode 100644 springboot/src/main/java/com/softlabs/aicontents/scheduler/service/BlogPublishExecutor.java create mode 100644 springboot/src/main/java/com/softlabs/aicontents/scheduler/service/KeywordExecutor.java create mode 100644 springboot/src/main/java/com/softlabs/aicontents/scheduler/service/ProductCrawlingExecutor.java delete mode 100644 springboot/src/main/java/com/softlabs/aicontents/scheduler/service/SchedulerService.java delete mode 100644 springboot/src/main/java/com/softlabs/aicontents/scheduler/vo/PipelineExecutionRequestVO.java delete mode 100644 springboot/src/main/java/com/softlabs/aicontents/scheduler/vo/PipelineExecutionResponseVO.java delete mode 100644 springboot/src/main/java/com/softlabs/aicontents/scheduler/vo/ScheduledRequestVO.java delete mode 100644 springboot/src/main/java/com/softlabs/aicontents/scheduler/vo/ScheduledResponseVO.java diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/f_001/controller/TestController.java b/springboot/src/main/java/com/softlabs/aicontents/domain/f_001/controller/TestController.java index 135aa0f2..cba92c72 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/domain/f_001/controller/TestController.java +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/f_001/controller/TestController.java @@ -1,4 +1,4 @@ -package com.softlabs.aicontents.domain.f_001.controller; - -public class TestController { -} +//package com.softlabs.aicontents.domain.f_001.controller; +// +//public class TestController { +//} diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/f_001/dto/request/TestReqDto.java b/springboot/src/main/java/com/softlabs/aicontents/domain/f_001/dto/request/TestReqDto.java index 916923b5..1b596f60 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/domain/f_001/dto/request/TestReqDto.java +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/f_001/dto/request/TestReqDto.java @@ -1,4 +1,4 @@ -package com.softlabs.aicontents.domain.f_001.dto.request; - -public class TestReqDto { -} +//package com.softlabs.aicontents.domain.f_001.dto.request; +// +//public class TestReqDto { +//} diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/f_001/dto/response/TestResDto.java b/springboot/src/main/java/com/softlabs/aicontents/domain/f_001/dto/response/TestResDto.java index e050c6e1..6529abb0 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/domain/f_001/dto/response/TestResDto.java +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/f_001/dto/response/TestResDto.java @@ -1,4 +1,4 @@ -package com.softlabs.aicontents.domain.f_001.dto.response; - -public class TestResDto { -} +//package com.softlabs.aicontents.domain.f_001.dto.response; +// +//public class TestResDto { +//} diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/f_001/mapper/TestMapper.java b/springboot/src/main/java/com/softlabs/aicontents/domain/f_001/mapper/TestMapper.java index 51a0c46f..e144e3ff 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/domain/f_001/mapper/TestMapper.java +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/f_001/mapper/TestMapper.java @@ -1,4 +1,4 @@ -package com.softlabs.aicontents.domain.f_001.mapper; - -public class TestMapper { -} +//package com.softlabs.aicontents.domain.f_001.mapper; +// +//public class TestMapper { +//} diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/f_001/service/TestService.java b/springboot/src/main/java/com/softlabs/aicontents/domain/f_001/service/TestService.java index 96ce7c3b..dd76556e 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/domain/f_001/service/TestService.java +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/f_001/service/TestService.java @@ -1,4 +1,4 @@ -package com.softlabs.aicontents.domain.f_001.service; - -public class TestService { -} +//package com.softlabs.aicontents.domain.f_001.service; +// +//public class TestService { +//} diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/f_001/vo/request/TestReqVo.java b/springboot/src/main/java/com/softlabs/aicontents/domain/f_001/vo/request/TestReqVo.java index c6545dbc..a7184bae 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/domain/f_001/vo/request/TestReqVo.java +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/f_001/vo/request/TestReqVo.java @@ -1,4 +1,4 @@ -package com.softlabs.aicontents.domain.f_001.vo.request; - -public class TestReqVo { -} +//package com.softlabs.aicontents.domain.f_001.vo.request; +// +//public class TestReqVo { +//} diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/f_001/vo/response/TestResVo.java b/springboot/src/main/java/com/softlabs/aicontents/domain/f_001/vo/response/TestResVo.java index 42e324e9..e182392c 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/domain/f_001/vo/response/TestResVo.java +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/f_001/vo/response/TestResVo.java @@ -1,4 +1,4 @@ -package com.softlabs.aicontents.domain.f_001.vo.response; - -public class TestResVo { -} +//package com.softlabs.aicontents.domain.f_001.vo.response; +// +//public class TestResVo { +//} diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/testMapper/AIContentMapper.java b/springboot/src/main/java/com/softlabs/aicontents/domain/testMapper/AIContentMapper.java new file mode 100644 index 00000000..b8f85d00 --- /dev/null +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/testMapper/AIContentMapper.java @@ -0,0 +1,12 @@ +package com.softlabs.aicontents.domain.testMapper; + +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface AIContentMapper { + + String findAicontentByExecutionId(Long executionId); + +} + +/// todo : 실제 개발한 Mapper 적용 \ No newline at end of file diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/testMapper/BlogPublishMapper.java b/springboot/src/main/java/com/softlabs/aicontents/domain/testMapper/BlogPublishMapper.java new file mode 100644 index 00000000..fdc6b2ac --- /dev/null +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/testMapper/BlogPublishMapper.java @@ -0,0 +1,12 @@ +package com.softlabs.aicontents.domain.testMapper; + + +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface BlogPublishMapper { + String findBlogPublishByExecutionId(Long executionId); +} + + +/// todo : 실제 개발한 Mapper 적용 \ No newline at end of file diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/testMapper/KeywordMapper.java b/springboot/src/main/java/com/softlabs/aicontents/domain/testMapper/KeywordMapper.java new file mode 100644 index 00000000..5fcb564f --- /dev/null +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/testMapper/KeywordMapper.java @@ -0,0 +1,11 @@ +package com.softlabs.aicontents.domain.testMapper; + +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface KeywordMapper { + String findKeywordByExecutionId(Long executionId); +} + + +/// todo : 실제 개발한 Mapper 적용 \ No newline at end of file diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/testMapper/ProductCrawlingMapper.java b/springboot/src/main/java/com/softlabs/aicontents/domain/testMapper/ProductCrawlingMapper.java new file mode 100644 index 00000000..a84c20c9 --- /dev/null +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/testMapper/ProductCrawlingMapper.java @@ -0,0 +1,13 @@ +package com.softlabs.aicontents.domain.testMapper; + +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface ProductCrawlingMapper { + String findproductCrawlingByExecutionId(Long executionId); + + } + + + +/// todo : 실제 개발한 Mapper 적용 \ No newline at end of file diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/testService/AIContentService.java b/springboot/src/main/java/com/softlabs/aicontents/domain/testService/AIContentService.java new file mode 100644 index 00000000..9713f907 --- /dev/null +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/testService/AIContentService.java @@ -0,0 +1,14 @@ +package com.softlabs.aicontents.domain.testService; + + +import org.springframework.stereotype.Service; + +@Service +public class AIContentService { + + public void extractAiContent(Long executionId) { + } +} + + +///todo : 실제 구현 클래스 적용 \ No newline at end of file diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/testService/BlogPublishService.java b/springboot/src/main/java/com/softlabs/aicontents/domain/testService/BlogPublishService.java new file mode 100644 index 00000000..08589b5a --- /dev/null +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/testService/BlogPublishService.java @@ -0,0 +1,14 @@ +package com.softlabs.aicontents.domain.testService; + + +import org.springframework.stereotype.Service; + +@Service +public class BlogPublishService { + public void extractBlogPublish(Long executionId) { + + } +} + + +///todo : 실제 구현 클래스 적용 \ No newline at end of file diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/testService/KeywordService.java b/springboot/src/main/java/com/softlabs/aicontents/domain/testService/KeywordService.java new file mode 100644 index 00000000..6bb03731 --- /dev/null +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/testService/KeywordService.java @@ -0,0 +1,13 @@ +package com.softlabs.aicontents.domain.testService; + +import org.springframework.stereotype.Service; + +@Service +public class KeywordService { + public void extractTrendKeyword(Long executionId) { + + } +} + + +///todo : 실제 구현 클래스 적용 \ No newline at end of file diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/testService/ProductCrawlingService.java b/springboot/src/main/java/com/softlabs/aicontents/domain/testService/ProductCrawlingService.java new file mode 100644 index 00000000..47364f13 --- /dev/null +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/testService/ProductCrawlingService.java @@ -0,0 +1,12 @@ +package com.softlabs.aicontents.domain.testService; + +import org.springframework.stereotype.Service; + +@Service +public class ProductCrawlingService { + public void extractproductCrawling(Long executionId) { + } +} + + +///todo : 실제 구현 클래스 적용 \ No newline at end of file diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/testexample/controller/TestExampleController.java b/springboot/src/main/java/com/softlabs/aicontents/domain/testexample/controller/TestExampleController.java index 58238cf1..1f70535e 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/domain/testexample/controller/TestExampleController.java +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/testexample/controller/TestExampleController.java @@ -1,28 +1,28 @@ -package com.softlabs.aicontents.domain.testexample.controller; - -import com.softlabs.aicontents.domain.testexample.service.TestExampleService; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; -import io.swagger.v3.oas.annotations.Operation; - -@RestController -@RequestMapping("/api/test-example") -public class TestExampleController { - - private final TestExampleService testExampleService; - - public TestExampleController(TestExampleService testExampleService) { - this.testExampleService = testExampleService; - } - - @PostMapping - @Operation(summary = "테스트 데이터 삽입", description = "RDS 테이블에 테스트 데이터를 삽입합니다.") - public ResponseEntity createTestExample(@RequestParam String testData) { - try { - testExampleService.createTestExample(testData); - return ResponseEntity.ok("테스트 데이터가 성공적으로 삽입되었습니다: " + testData); - } catch (Exception e) { - return ResponseEntity.status(500).body("데이터 삽입 실패: " + e.getMessage()); - } - } -} \ No newline at end of file +//package com.softlabs.aicontents.domain.testexample.controller; +// +//import com.softlabs.aicontents.domain.testexample.service.TestExampleService; +//import org.springframework.http.ResponseEntity; +//import org.springframework.web.bind.annotation.*; +//import io.swagger.v3.oas.annotations.Operation; +// +//@RestController +//@RequestMapping("/api/test-example") +//public class TestExampleController { +// +// private final TestExampleService testExampleService; +// +// public TestExampleController(TestExampleService testExampleService) { +// this.testExampleService = testExampleService; +// } +// +// @PostMapping +// @Operation(summary = "테스트 데이터 삽입", description = "RDS 테이블에 테스트 데이터를 삽입합니다.") +// public ResponseEntity createTestExample(@RequestParam String testData) { +// try { +// testExampleService.createTestExample(testData); +// return ResponseEntity.ok("테스트 데이터가 성공적으로 삽입되었습니다: " + testData); +// } catch (Exception e) { +// return ResponseEntity.status(500).body("데이터 삽입 실패: " + e.getMessage()); +// } +// } +//} \ No newline at end of file diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/testexample/entity/TestExample.java b/springboot/src/main/java/com/softlabs/aicontents/domain/testexample/entity/TestExample.java index f923f614..ed8384f1 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/domain/testexample/entity/TestExample.java +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/testexample/entity/TestExample.java @@ -1,28 +1,28 @@ -package com.softlabs.aicontents.domain.testexample.entity; - -public class TestExample { - private Long id; - private String testData; - - public TestExample() {} - - public TestExample(String testData) { - this.testData = testData; - } - - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public String getTestData() { - return testData; - } - - public void setTestData(String testData) { - this.testData = testData; - } -} \ No newline at end of file +//package com.softlabs.aicontents.domain.testexample.entity; +// +//public class TestExample { +// private Long id; +// private String testData; +// +// public TestExample() {} +// +// public TestExample(String testData) { +// this.testData = testData; +// } +// +// public Long getId() { +// return id; +// } +// +// public void setId(Long id) { +// this.id = id; +// } +// +// public String getTestData() { +// return testData; +// } +// +// public void setTestData(String testData) { +// this.testData = testData; +// } +//} \ No newline at end of file diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/testexample/mapper/TestExampleMapper.java b/springboot/src/main/java/com/softlabs/aicontents/domain/testexample/mapper/TestExampleMapper.java index 318838ee..7c6cba17 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/domain/testexample/mapper/TestExampleMapper.java +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/testexample/mapper/TestExampleMapper.java @@ -1,9 +1,9 @@ -package com.softlabs.aicontents.domain.testexample.mapper; - -import com.softlabs.aicontents.domain.testexample.entity.TestExample; -import org.apache.ibatis.annotations.Mapper; - -@Mapper -public interface TestExampleMapper { - void insertTestExample(TestExample testExample); -} \ No newline at end of file +//package com.softlabs.aicontents.domain.testexample.mapper; +// +//import com.softlabs.aicontents.domain.testexample.entity.TestExample; +//import org.apache.ibatis.annotations.Mapper; +// +//@Mapper +//public interface TestExampleMapper { +// void insertTestExample(TestExample testExample); +//} \ No newline at end of file diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/testexample/service/TestExampleService.java b/springboot/src/main/java/com/softlabs/aicontents/domain/testexample/service/TestExampleService.java index 4d511dbb..93273f6d 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/domain/testexample/service/TestExampleService.java +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/testexample/service/TestExampleService.java @@ -1,20 +1,20 @@ -package com.softlabs.aicontents.domain.testexample.service; - -import com.softlabs.aicontents.domain.testexample.entity.TestExample; -import com.softlabs.aicontents.domain.testexample.mapper.TestExampleMapper; -import org.springframework.stereotype.Service; - -@Service -public class TestExampleService { - - private final TestExampleMapper testExampleMapper; - - public TestExampleService(TestExampleMapper testExampleMapper) { - this.testExampleMapper = testExampleMapper; - } - - public void createTestExample(String testData) { - TestExample testExample = new TestExample(testData); - testExampleMapper.insertTestExample(testExample); - } -} \ No newline at end of file +//package com.softlabs.aicontents.domain.testexample.service; +// +//import com.softlabs.aicontents.domain.testexample.entity.TestExample; +//import com.softlabs.aicontents.domain.testexample.mapper.TestExampleMapper; +//import org.springframework.stereotype.Service; +// +//@Service +//public class TestExampleService { +// +// private final TestExampleMapper testExampleMapper; +// +// public TestExampleService(TestExampleMapper testExampleMapper) { +// this.testExampleMapper = testExampleMapper; +// } +// +// public void createTestExample(String testData) { +// TestExample testExample = new TestExample(testData); +// testExampleMapper.insertTestExample(testExample); +// } +//} \ No newline at end of file diff --git a/springboot/src/main/java/com/softlabs/aicontents/health/HealthController.java b/springboot/src/main/java/com/softlabs/aicontents/health/HealthController.java index 38883083..ab87ec34 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/health/HealthController.java +++ b/springboot/src/main/java/com/softlabs/aicontents/health/HealthController.java @@ -1,35 +1,35 @@ -package com.softlabs.aicontents.health; - -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; -import java.util.Map; - -@RestController -@RequestMapping("/api/health") -public class HealthController { - - private final HealthService healthService; - - public HealthController(HealthService healthService) { - this.healthService = healthService; - } - - @GetMapping - public ResponseEntity> checkHealth() { - Map healthInfo = healthService.checkHealth(); - - // 연결 상태에 따라 HTTP 상태 코드 설정 - if ("UP".equals(healthInfo.get("status"))) { - return ResponseEntity.ok(healthInfo); - } else { - return ResponseEntity.status(503).body(healthInfo); - } - } - - @GetMapping("/db") - public ResponseEntity> checkDatabase() { - return checkHealth(); - } -} +//package com.softlabs.aicontents.health; +// +//import org.springframework.http.ResponseEntity; +//import org.springframework.web.bind.annotation.GetMapping; +//import org.springframework.web.bind.annotation.RequestMapping; +//import org.springframework.web.bind.annotation.RestController; +//import java.util.Map; +// +//@RestController +//@RequestMapping("/api/health") +//public class HealthController { +// +// private final HealthService healthService; +// +// public HealthController(HealthService healthService) { +// this.healthService = healthService; +// } +// +// @GetMapping +// public ResponseEntity> checkHealth() { +// Map healthInfo = healthService.checkHealth(); +// +// // 연결 상태에 따라 HTTP 상태 코드 설정 +// if ("UP".equals(healthInfo.get("status"))) { +// return ResponseEntity.ok(healthInfo); +// } else { +// return ResponseEntity.status(503).body(healthInfo); +// } +// } +// +// @GetMapping("/db") +// public ResponseEntity> checkDatabase() { +// return checkHealth(); +// } +//} diff --git a/springboot/src/main/java/com/softlabs/aicontents/health/HealthMapper.java b/springboot/src/main/java/com/softlabs/aicontents/health/HealthMapper.java index 56bc3099..bdadd668 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/health/HealthMapper.java +++ b/springboot/src/main/java/com/softlabs/aicontents/health/HealthMapper.java @@ -1,8 +1,8 @@ -package com.softlabs.aicontents.health; - -import org.apache.ibatis.annotations.Mapper; - -@Mapper -public interface HealthMapper { - String getSysdate(); -} +//package com.softlabs.aicontents.health; +// +//import org.apache.ibatis.annotations.Mapper; +// +//@Mapper +//public interface HealthMapper { +// String getSysdate(); +//} diff --git a/springboot/src/main/java/com/softlabs/aicontents/health/HealthService.java b/springboot/src/main/java/com/softlabs/aicontents/health/HealthService.java index c61a3b54..b3bef9a4 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/health/HealthService.java +++ b/springboot/src/main/java/com/softlabs/aicontents/health/HealthService.java @@ -1,41 +1,41 @@ -package com.softlabs.aicontents.health; - -import org.springframework.stereotype.Service; -import java.util.HashMap; -import java.util.Map; - -@Service -public class HealthService { - - private final HealthMapper healthMapper; - - public HealthService(HealthMapper healthMapper) { - this.healthMapper = healthMapper; - } - - public Map checkHealth() { - Map healthInfo = new HashMap<>(); - - try { - // RDS 연결 확인을 위해 현재 시간 조회 - String currentTime = healthMapper.getSysdate(); - - healthInfo.put("status", "UP"); - healthInfo.put("database", "Oracle RDS"); - healthInfo.put("connection", "SUCCESS"); - healthInfo.put("currentTime", currentTime); - healthInfo.put("message", "RDS 연결이 정상적으로 작동중입니다."); - - } catch (Exception e) { - e.printStackTrace(); // 콘솔에 상세 에러 출력 - healthInfo.put("status", "DOWN"); - healthInfo.put("database", "Oracle RDS"); - healthInfo.put("connection", "FAILED"); - healthInfo.put("error", e.getMessage()); - healthInfo.put("errorClass", e.getClass().getSimpleName()); - healthInfo.put("message", "RDS 연결에 실패했습니다."); - } - - return healthInfo; - } -} +//package com.softlabs.aicontents.health; +// +//import org.springframework.stereotype.Service; +//import java.util.HashMap; +//import java.util.Map; +// +//@Service +//public class HealthService { +// +// private final HealthMapper healthMapper; +// +// public HealthService(HealthMapper healthMapper) { +// this.healthMapper = healthMapper; +// } +// +// public Map checkHealth() { +// Map healthInfo = new HashMap<>(); +// +// try { +// // RDS 연결 확인을 위해 현재 시간 조회 +// String currentTime = healthMapper.getSysdate(); +// +// healthInfo.put("status", "UP"); +// healthInfo.put("database", "Oracle RDS"); +// healthInfo.put("connection", "SUCCESS"); +// healthInfo.put("currentTime", currentTime); +// healthInfo.put("message", "RDS 연결이 정상적으로 작동중입니다."); +// +// } catch (Exception e) { +// e.printStackTrace(); // 콘솔에 상세 에러 출력 +// healthInfo.put("status", "DOWN"); +// healthInfo.put("database", "Oracle RDS"); +// healthInfo.put("connection", "FAILED"); +// healthInfo.put("error", e.getMessage()); +// healthInfo.put("errorClass", e.getClass().getSimpleName()); +// healthInfo.put("message", "RDS 연결에 실패했습니다."); +// } +// +// return healthInfo; +// } +//} diff --git a/springboot/src/main/java/com/softlabs/aicontents/log/TestLog.java b/springboot/src/main/java/com/softlabs/aicontents/log/TestLog.java index 461eee84..a35aa962 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/log/TestLog.java +++ b/springboot/src/main/java/com/softlabs/aicontents/log/TestLog.java @@ -1,4 +1,4 @@ -package com.softlabs.aicontents.log; - -public class TestLog { -} +//package com.softlabs.aicontents.log; +// +//public class TestLog { +//} diff --git a/springboot/src/main/java/com/softlabs/aicontents/orchestration/enums/PipelineStep.java b/springboot/src/main/java/com/softlabs/aicontents/orchestration/enums/PipelineStep.java index e9ec0c7b..80a9edb7 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/orchestration/enums/PipelineStep.java +++ b/springboot/src/main/java/com/softlabs/aicontents/orchestration/enums/PipelineStep.java @@ -1,6 +1,20 @@ package com.softlabs.aicontents.orchestration.enums; -public class PipelineStep { - //파이프라인 단계 정의 +public enum PipelineStep { + STEP_01("STEP_01", "구글 트렌드 키워드 수집", 1), + STEP_02("STEP_02", "싸다구몰 상품 수집", 2), + STEP_03("STEP_03", "AI 콘텐츠 생성", 3), + STEP_04("STEP_04", "네이버 블로그 업로드", 4); + + private final String code; + private final String description; + private final int order; + + PipelineStep(String code, String description, int order) { + this.code = code; + this.description = description; + this.order = order; + } + } diff --git a/springboot/src/main/java/com/softlabs/aicontents/scheduler/ContentAutomationScheduler.java b/springboot/src/main/java/com/softlabs/aicontents/scheduler/ContentAutomationScheduler.java deleted file mode 100644 index 4bb31725..00000000 --- a/springboot/src/main/java/com/softlabs/aicontents/scheduler/ContentAutomationScheduler.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.softlabs.aicontents.scheduler; - - -import org.springframework.scheduling.annotation.EnableScheduling; -import org.springframework.stereotype.Component; - -@EnableScheduling -@Component -public class ContentAutomationScheduler { -} diff --git a/springboot/src/main/java/com/softlabs/aicontents/scheduler/controller/ExecutionContext.java b/springboot/src/main/java/com/softlabs/aicontents/scheduler/controller/ExecutionContext.java deleted file mode 100644 index 116b1dd7..00000000 --- a/springboot/src/main/java/com/softlabs/aicontents/scheduler/controller/ExecutionContext.java +++ /dev/null @@ -1,102 +0,0 @@ -package com.softlabs.aicontents.scheduler.controller; - -import com.softlabs.aicontents.scheduler.mapper.SchedulerMapper; - -public class ExecutionContext { - - // 실행 식별자 - private Long executionId; - private Long taskId; - private String currentStepName; - - // DB 접근용 - private SchedulerMapper scheduledMapper; - - // 생성자 - public ExecutionContext(SchedulerMapper scheduledMapper) { - this.scheduledMapper = scheduledMapper; - } - - /** - * 실행 시작시 호출 - DB에 실행 기록 생성 - */ - public void initialize(Long taskId) { - this.taskId = taskId; - - // TODO: PIPELINE_EXECUTIONS 테이블에 새 실행 기록 생성 - // TODO: executionId 설정 - - System.out.println("ExecutionContext 초기화 완료 - TaskID: " + taskId); - } - - /** - * 스케줄 작업 설정값 조회 - */ - public String getTaskSetting(String settingKey) { - // TODO: SCHEDULED_TASKS 테이블에서 설정 조회 - - switch(settingKey) { - case "target_keywords": - return "기본키워드1,기본키워드2"; // 임시 하드코딩 - case "pipeline_config": - return "{}"; // 임시 빈 JSON - default: - return null; - } - } - - /** - * 이전 단계 결과 조회 - */ - public String getPreviousStepResult(String stepName) { - // TODO: STEP_RESULTS 테이블에서 해당 단계의 output_data 조회 - - System.out.println("이전 단계 결과 조회: " + stepName); - return "임시_이전단계_결과_데이터"; // 임시 하드코딩 - } - - /** - * 현재 단계 결과 저장 - */ - public void saveCurrentStepResult(String stepName, String result) { - this.currentStepName = stepName; - - // TODO: STEP_RESULTS 테이블에 결과 저장 - // TODO: PIPELINE_EXECUTIONS 상태 업데이트 - - System.out.println("단계 결과 저장 - " + stepName + ": " + result.substring(0, Math.min(50, result.length()))); - } - - /** - * 실행 완료 처리 - */ - public void markCompleted() { - // TODO: PIPELINE_EXECUTIONS 상태를 'COMPLETED'로 변경 - // TODO: end_time 설정 - - System.out.println("실행 완료 처리 - ExecutionID: " + executionId); - } - - /** - * 실행 실패 처리 - */ - public void markFailed(String errorMessage) { - // TODO: PIPELINE_EXECUTIONS 상태를 'FAILED'로 변경 - // TODO: error_message 저장 - - System.out.println("실행 실패 처리 - " + errorMessage); - } - - // Getters - public Long getExecutionId() { - return executionId; - } - - public Long getTaskId() { - return taskId; - } - - public String getCurrentStepName() { - return currentStepName; - } -} \ No newline at end of file diff --git a/springboot/src/main/java/com/softlabs/aicontents/scheduler/controller/PipelineOrchestrator.java b/springboot/src/main/java/com/softlabs/aicontents/scheduler/controller/PipelineOrchestrator.java deleted file mode 100644 index 2fc97455..00000000 --- a/springboot/src/main/java/com/softlabs/aicontents/scheduler/controller/PipelineOrchestrator.java +++ /dev/null @@ -1,204 +0,0 @@ -package com.softlabs.aicontents.scheduler.controller; - - -import com.softlabs.aicontents.scheduler.mapper.SchedulerMapper; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.exception.ExceptionUtils; -import org.apache.coyote.http11.filters.SavedRequestInputFilter; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Service; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -@Service -@Slf4j -public class PipelineOrchestrator { - - private final SchedulerMapper schedulerMapper; - - public PipelineOrchestrator(SchedulerMapper schedulerMapper) { - this.schedulerMapper = schedulerMapper; - - } - - - public void executePipeline(Long taskId) { - ExecutionContext context = new ExecutionContext(schedulerMapper); - - try{ - log.info("파이프라인 실행 시작 - TaskID: {}", taskId); - - //컨택스트 초기화 - context.initialize(taskId); - - //1단계 : 트랜드 크롤링 확인 - String step1Result= executeStep1(context); - context.saveCurrentStepResult("TREND_CRAWLING", step1Result); - - //2단계 : 컨탠츠 크롤링 - String step2Result = executeStep2(context); - context.saveCurrentStepResult("SSADAGU_CRAWLING", step2Result); - - //3단계 : AI 컨탠츠 생성 - String step3Result = executeStep3(context); - context.saveCurrentStepResult("AI_GENERATION", step3Result); - - // 4단계: 블로그 발행 - String step4Result = executeStep4(context); - context.saveCurrentStepResult("BLOG_PUBLISH", step4Result); - - // 완료 처리 - context.markCompleted(); - log.info("파이프라인 실행 완료 - TaskID: {}", taskId); - - } catch(Exception e) { - log.error("파이프라인 실행실패 - TaskID: {}, 오류: {}", taskId,e.getMessage(), e); - context.markFailed(e.getMessage()); - } - } - - // 메서드 - - //1단계 - private String executeStep1(ExecutionContext context){ - log.info("1단계 시작 : 트랜드 크롤링"); - - try{ - //설정 조회 - String keywords = context.getTaskSetting("target_keywords"); - log.info("크롤링 키워드 : {}",keywords); - - // TODO: 실제 TrendService 호출 - // TrendService trendService = new TrendService(); - // List results = trendService.crawl(Arrays.asList(keywords.split(","))); - - // 임시 Mock 데이터 - String mockResult = "[\"" + keywords + " 트렌드1\", \"" + keywords + " 트렌드2\", \"최신 이슈\"]"; - - log.info("1단계 완료: 트렌드 데이터 수집됨"); - return mockResult; - - } catch(Exception e){ - log.error("1단계 실패:{}",e.getMessage()); - throw new RuntimeException("트랜드 크롤링 실해:" +e.getMessage()); - } - - - } - /** - * 2단계: 콘텐츠 크롤링 - */ - private String executeStep2(ExecutionContext context) { - log.info("2단계 시작: 콘텐츠 크롤링"); - - try { - // 이전 단계 결과 조회 - String step1Data = context.getPreviousStepResult("TREND_CRAWLING"); - log.info("1단계 결과 활용: {}", step1Data); - - // TODO: 실제 SsadaguService 호출 - // SsadaguService ssadaguService = new SsadaguService(); - // List
results = ssadaguService.crawl(parseKeywords(step1Data)); - - // 임시 Mock 데이터 - String mockResult = "[{\"title\":\"관련 기사1\", \"content\":\"내용1\"}, {\"title\":\"관련 기사2\", \"content\":\"내용2\"}]"; - - log.info("2단계 완료: 컨텐츠 크롤링 완료"); - return mockResult; - - } catch (Exception e) { - log.error("2단계 실패: {}", e.getMessage()); - throw new RuntimeException("콘텐츠 크롤링 실패: " + e.getMessage()); - } - } - - /** - * 3단계: AI 콘텐츠 생성 - */ - private String executeStep3(ExecutionContext context) { - log.info("3단계 시작: AI 콘텐츠 생성"); - - try { - // 이전 단계들 결과 조회 - String step1Data = context.getPreviousStepResult("TREND_CRAWLING"); - String step2Data = context.getPreviousStepResult("SSADAGU_CRAWLING"); - - log.info("1,2단계 결과 활용 - 트렌드: {}, 크롤링: {}", - step1Data.substring(0, Math.min(50, step1Data.length())), - step2Data.substring(0, Math.min(50, step2Data.length()))); - - // TODO: 실제 AiService 호출 - // AiService aiService = new AiService(); - // String result = aiService.generateContent(step1Data, step2Data); - - // 임시 Mock 데이터 - String mockResult = "{\"title\":\"AI가 생성한 제목\", \"content\":\"AI가 생성한 블로그 내용...\", \"tags\":[\"태그1\", \"태그2\"]}"; - - log.info("3단계 완료: AI 콘텐츠 생성 완료"); - return mockResult; - - }catch (Exception e){ - log.error("3단계 실패: {} ",e.getMessage()); - throw new RuntimeException("AI 콘텐츠 생성 실패: " + e.getMessage()); - } - } - - /** - * 4단계: 블로그 발행 - */ - private String executeStep4(ExecutionContext context) { - log.info("4단계 시작: 블로그 발행"); - - try { - // 이전 단계 결과 조회 - String step3Data = context.getPreviousStepResult("AI_GENERATION"); - log.info("3단계 결과 활용: {}", step3Data.substring(0, Math.min(50, step3Data.length()))); - - // TODO: 실제 BlogService 호출 - // BlogService blogService = new BlogService(); - // String result = blogService.publish(step3Data); - - // 임시 Mock 데이터 - String mockResult = "{\"blog_url\":\"https://blog.example.com/post123\", \"post_id\":\"123\", \"status\":\"published\"}"; - - log.info("4단계 완료: 블로그 발행 완료"); - return mockResult; - - } catch (Exception e) { - log.error("4단계 실패: {} ", e.getMessage()); - throw new RuntimeException("블로그 발행 실패: " + e.getMessage()); - } - } - - /** - * 테스트용 컨트롤러 - */ - @RestController - @RequestMapping("/api/test") - public class PipelineTestController { - - private final PipelineOrchestrator pipelineOrchestrator; - - public PipelineTestController(PipelineOrchestrator pipelineOrchestrator) { - this.pipelineOrchestrator = pipelineOrchestrator; - } - - /** - * 파이프라인 수동 실행 테스트 - */ - @PostMapping("/execute/{taskId}") - public ResponseEntity testPipelineExecution(@PathVariable Long taskId) { - try { - pipelineOrchestrator.executePipeline(taskId); - return ResponseEntity.ok("파이프라인 실행 완료"); - } catch (Exception e) { - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) - .body("파이프라인 실행 실패: " + e.getMessage()); - } - } - } - - } diff --git a/springboot/src/main/java/com/softlabs/aicontents/scheduler/controller/PipelineService.java b/springboot/src/main/java/com/softlabs/aicontents/scheduler/controller/PipelineService.java new file mode 100644 index 00000000..9b8177f9 --- /dev/null +++ b/springboot/src/main/java/com/softlabs/aicontents/scheduler/controller/PipelineService.java @@ -0,0 +1,83 @@ +package com.softlabs.aicontents.scheduler.controller; + +import com.softlabs.aicontents.scheduler.dto.pipeLineDTO.StepExecutionResultDTO; +import com.softlabs.aicontents.scheduler.service.AIContentExecutor; +import com.softlabs.aicontents.scheduler.service.ProductCrawlingExecutor; +import com.softlabs.aicontents.scheduler.service.KeywordExecutor; +import com.softlabs.aicontents.scheduler.service.BlogPublishExecutor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +@Component +public class PipelineService { + + + // 🎯 실행 인터페이스들만 주입 + @Autowired + private KeywordExecutor keywordExecutor; + + @Autowired + private ProductCrawlingExecutor crawlingExecutor; + + @Autowired + private AIContentExecutor aiExecutor; + + @Autowired + private BlogPublishExecutor blogExecutor; + + public void executionPipline(){ + Long executionId = createNewExecution(); + //todo : executionId = (동일한 파이프라인인지 구분하는 용도) + //DB 에서 PIPELINE_EXECUTIONS 테이블의 execution_id + + try { /// 파이프라인 전체 try-catch + // 각 단계를 순차적으로 실행 (실행과 검증이 포함되어 있음) + + //step01 - 키워드 추출 + StepExecutionResultDTO step01 = keywordExecutor.execute(executionId); + //todo : if 추출 실패 시 3회 재시도 및 예외처리 + // 예시 : + // if (!step1.isSuccess()) { + // throw new RuntimeException("1단계 실패: " + step1.getErrorMessage()); + + //step02 - 상품정보 & URL 추출 + StepExecutionResultDTO step02 = crawlingExecutor.execute(executionId); + //todo : if 추출 실패 시 3회 재시도 및 예외처리 + + //step03 - LLM 생성 + StepExecutionResultDTO step03 = aiExecutor.execute(executionId); + //todo : if 추출 실패 시 3회 재시도 및 예외처리 + + //step04 - 블로그 발행 + StepExecutionResultDTO step04 = blogExecutor.execute(executionId); + //todo : if 추출 실패 시 3회 재시도 및 예외처리 + + log.error("파이프라인 성공"); + + } catch (Exception e){ + log.error("파이프라인 실행 실패:{}",e.getMessage()); + updateExecutionStatus(executionId, "FAILED"); + } + + } + + private Long createNewExecution() { + + return 0L; ///일단은 Long타입의 기본값. + //todo: return 반환값으로, + // PIPELINE_EXECUTIONS 테이블에서 executionId를 새로 생성하고, + // 이것을 가져오는 메서드 구현 + // => 파이프라인이 새로 실행될 때마다 executionId를 생성 + } + + private void updateExecutionStatus(Long executionId, String failed) { + //todo: PIPELINE_EXECUTIONS에 상태 업데이트하는 코드 구현(SUCCESS, FAILED,PENDING 등등등) + } +} + + + diff --git a/springboot/src/main/java/com/softlabs/aicontents/scheduler/controller/SchedulerController.java b/springboot/src/main/java/com/softlabs/aicontents/scheduler/controller/SchedulerController.java deleted file mode 100644 index d03782ed..00000000 --- a/springboot/src/main/java/com/softlabs/aicontents/scheduler/controller/SchedulerController.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.softlabs.aicontents.scheduler.controller; - -import com.softlabs.aicontents.scheduler.service.ScheduledTaskService; - -import org.springframework.web.bind.annotation.*; - -@RestController -// REST API를 처리하는 컨트롤러이다. -@RequestMapping("/api") -//이 클래스의 모든 URL은 /api로 시작한다 -@CrossOrigin(origins = "*") -//모든 도메인(="*")에서 이 API 호출 허용 - -public class SchedulerController { - - private final ScheduledTaskService scheduledTaskService; - - public SchedulerController(ScheduledTaskService scheduledTaskService) { - this.scheduledTaskService = scheduledTaskService; - } - //scheduledTaskService 타입의 변수 선언 - - //더미 데이터 제거 -> 실제 DB 조회 - @GetMapping("/tasks") //스케줄 목록을 조회 - //GET 방식으로 /api/test URL 요청이 오면 이 메서드 실행 - public String testConnection(){ - //테스트 메서드 선언 - int count = scheduledTaskService.getTaskCount(); - //service의 getTaskCount() 메서드 호출해서 결과를 count 변수에 저장 - - return "RDS 연결 성공 TEST_SCHEDULED_TASKS 테이블에" + count + "개 조회됨"; - - } -} diff --git a/springboot/src/main/java/com/softlabs/aicontents/scheduler/dto/ScheduleRequestDTO.java b/springboot/src/main/java/com/softlabs/aicontents/scheduler/dto/ScheduleRequestDTO.java deleted file mode 100644 index 12912c7e..00000000 --- a/springboot/src/main/java/com/softlabs/aicontents/scheduler/dto/ScheduleRequestDTO.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.softlabs.aicontents.scheduler.dto; - -import com.fasterxml.jackson.annotation.JsonProperty; - -public record ScheduleRequestDTO( - @JsonProperty("taskId") Long taskId, - @JsonProperty("taskName") String taskName, - @JsonProperty("taskDescription") String taskDescription, - @JsonProperty("cronExpression") String cronExpression, - @JsonProperty("taskType") String taskType, - @JsonProperty("isActive") Boolean isActive, - @JsonProperty("nextExecution") String nextExecution, - @JsonProperty("lastExecution") String lastExecution, - @JsonProperty("status") String status -) { - -} \ No newline at end of file diff --git a/springboot/src/main/java/com/softlabs/aicontents/scheduler/dto/ScheduleResponseDTO.java b/springboot/src/main/java/com/softlabs/aicontents/scheduler/dto/ScheduleResponseDTO.java deleted file mode 100644 index b79a0072..00000000 --- a/springboot/src/main/java/com/softlabs/aicontents/scheduler/dto/ScheduleResponseDTO.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.softlabs.aicontents.scheduler.dto; - -public record ScheduleResponseDTO( - Long taskId, - String taskName, - String taskDescription, - String cronExpression, - String taskType, - boolean isActive, - String nextExecution, - String lastExecution, - String status -) {} \ No newline at end of file diff --git a/springboot/src/main/java/com/softlabs/aicontents/scheduler/dto/pipeLineDTO/StepExecutionResultDTO.java b/springboot/src/main/java/com/softlabs/aicontents/scheduler/dto/pipeLineDTO/StepExecutionResultDTO.java new file mode 100644 index 00000000..9795e146 --- /dev/null +++ b/springboot/src/main/java/com/softlabs/aicontents/scheduler/dto/pipeLineDTO/StepExecutionResultDTO.java @@ -0,0 +1,30 @@ +package com.softlabs.aicontents.scheduler.dto.pipeLineDTO; + + +import lombok.Getter; + +@Getter +public class StepExecutionResultDTO { + + private boolean success; + private String resultData; + private String errorMessage; + + // 생성자를 private으로 막아서 외부에서 new로 생성하는 것을 방지 + private StepExecutionResultDTO(boolean success, String resultData, String errorMessage) { + this.success = success; + this.resultData = resultData; + this.errorMessage = errorMessage; + } + + // 성공 결과를 생성하는 정적 메서드 + public static StepExecutionResultDTO success(String resultData) { + return new StepExecutionResultDTO(true, resultData, null); + } + + // 실패 결과를 생성하는 정적 메서드 + public static StepExecutionResultDTO failure(String errorMessage) { + return new StepExecutionResultDTO(false, null, errorMessage); + } +} + diff --git a/springboot/src/main/java/com/softlabs/aicontents/scheduler/interfacePipe/PipelineStepExecutor.java b/springboot/src/main/java/com/softlabs/aicontents/scheduler/interfacePipe/PipelineStepExecutor.java new file mode 100644 index 00000000..fd754353 --- /dev/null +++ b/springboot/src/main/java/com/softlabs/aicontents/scheduler/interfacePipe/PipelineStepExecutor.java @@ -0,0 +1,11 @@ +package com.softlabs.aicontents.scheduler.interfacePipe; + +import com.softlabs.aicontents.scheduler.dto.pipeLineDTO.StepExecutionResultDTO; + +public interface PipelineStepExecutor { +// 파이프라인 실행관련 공통 인터페이스 + + StepExecutionResultDTO execute(Long executionId); + +} + diff --git a/springboot/src/main/java/com/softlabs/aicontents/scheduler/mapper/ScheduledTaskMapper.java b/springboot/src/main/java/com/softlabs/aicontents/scheduler/mapper/ScheduledTaskMapper.java deleted file mode 100644 index b53ad923..00000000 --- a/springboot/src/main/java/com/softlabs/aicontents/scheduler/mapper/ScheduledTaskMapper.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.softlabs.aicontents.scheduler.mapper; - - -import org.apache.ibatis.annotations.Mapper; -import org.springframework.scheduling.config.ScheduledTask; - -import java.util.List; - -@Mapper -//이 인터페이스는 MyBatis가 관리하는 Mapper다 -public interface ScheduledTaskMapper { - //인터페이스 시작 - - int countTasks(); - //countTasks라는 메서드 선언, 반환 int - // 실제 구현은 xml에 있음. -} \ No newline at end of file diff --git a/springboot/src/main/java/com/softlabs/aicontents/scheduler/mapper/SchedulerMapper.java b/springboot/src/main/java/com/softlabs/aicontents/scheduler/mapper/SchedulerMapper.java deleted file mode 100644 index 5549a606..00000000 --- a/springboot/src/main/java/com/softlabs/aicontents/scheduler/mapper/SchedulerMapper.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.softlabs.aicontents.scheduler.mapper; - - -import com.softlabs.aicontents.scheduler.vo.ScheduledResponseVO; -import org.apache.ibatis.annotations.Mapper; -import org.apache.ibatis.annotations.Param; - -@Mapper -//이 인터페이스는 MyBatis가 관리하는 Mapper다 -public interface SchedulerMapper { - //인터페이스 시작 - - int countTasks(); - //countTasks라는 메서드 선언, 반환 int - // 실제 구현은 xml에 있음. - - ScheduledResponseVO selectByTaskId(@Param("taskId") Long taskId); - //파라미터 이름 지정: XML에서 #{taskId}로 사용할 수 있게 이름을 지정 - //입력 파라미터: 이 메서드가 받는 데이터 - -} \ No newline at end of file diff --git a/springboot/src/main/java/com/softlabs/aicontents/scheduler/service/AIContentExecutor.java b/springboot/src/main/java/com/softlabs/aicontents/scheduler/service/AIContentExecutor.java new file mode 100644 index 00000000..205b497d --- /dev/null +++ b/springboot/src/main/java/com/softlabs/aicontents/scheduler/service/AIContentExecutor.java @@ -0,0 +1,68 @@ +package com.softlabs.aicontents.scheduler.service; + + +import com.softlabs.aicontents.scheduler.dto.pipeLineDTO.StepExecutionResultDTO; +import com.softlabs.aicontents.scheduler.interfacePipe.PipelineStepExecutor; +import com.softlabs.aicontents.domain.testMapper.AIContentMapper; +import com.softlabs.aicontents.domain.testService.AIContentService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; + +@Component +@Slf4j +@Service +public class AIContentExecutor implements PipelineStepExecutor { + + @Autowired + private AIContentService aiContentService; + // todo: 실제 LLM생성 클래스로 변경 + + @Autowired + private AIContentMapper aiContentMapper; + // todo: 실제 LLM생성 매퍼 인터페이스로 변경 + + + @Override + public StepExecutionResultDTO execute(Long executionId) { + //execute02 실행 + + try { + //키워드 수집 서비스 실행 + log.info("LLM생성 메서스 시작"); + aiContentService.extractAiContent(executionId); + // todo: 실제 키워드 수집 서비스 의 추출 메서드 + + //DB 조회로 결과 확인 (30초 대기 적용) + String keyword = waitForResult(executionId, 30); + + if (keyword != null) { + log.info("✅ 트렌드 키워드 추출 완료: {}", keyword); + return StepExecutionResultDTO.success(keyword); + + } else { + return StepExecutionResultDTO.failure("트렌드 키워드 추출 시간 초과"); + } + + } catch (Exception e) { + log.error("트렌드 키워드 추출 실패", e); + return StepExecutionResultDTO.failure(e.getMessage()); + } + } + private String waitForResult(Long executionId, int timeoutSeconds) { + for (int i = 0; i < timeoutSeconds; i++) { + String keyword = aiContentMapper.findAicontentByExecutionId(executionId); + if (keyword != null) { + return keyword; + } + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + break; + } + } + return null; + } +} diff --git a/springboot/src/main/java/com/softlabs/aicontents/scheduler/service/BlogPublishExecutor.java b/springboot/src/main/java/com/softlabs/aicontents/scheduler/service/BlogPublishExecutor.java new file mode 100644 index 00000000..cc569358 --- /dev/null +++ b/springboot/src/main/java/com/softlabs/aicontents/scheduler/service/BlogPublishExecutor.java @@ -0,0 +1,68 @@ +package com.softlabs.aicontents.scheduler.service; + +import com.softlabs.aicontents.scheduler.dto.pipeLineDTO.StepExecutionResultDTO; +import com.softlabs.aicontents.scheduler.interfacePipe.PipelineStepExecutor; +import com.softlabs.aicontents.domain.testMapper.BlogPublishMapper; +import com.softlabs.aicontents.domain.testService.BlogPublishService; +import lombok.extern.slf4j.Slf4j; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; + + +@Component +@Slf4j +@Service +public class BlogPublishExecutor implements PipelineStepExecutor { + @Autowired + private BlogPublishService blogPublishService; + // todo: 실제 LLM생성 클래스로 변경 + + @Autowired + private BlogPublishMapper blogPublishMapper; + // todo: 실제 LLM생성 매퍼 인터페이스로 변경 + + + @Override + public StepExecutionResultDTO execute(Long executionId) { + + + try { + //키워드 수집 서비스 실행 + log.info("LLM생성 메서스 시작"); + blogPublishService.extractBlogPublish(executionId); + // todo: 실제 키워드 수집 서비스 의 추출 메서드 + + //DB 조회로 결과 확인 (30초 대기 적용) + String keyword = waitForResult(executionId, 30); + + if (keyword != null) { + log.info("✅ 트렌드 키워드 추출 완료: {}", keyword); + return StepExecutionResultDTO.success(keyword); + + } else { + return StepExecutionResultDTO.failure("트렌드 키워드 추출 시간 초과"); + } + + } catch (Exception e) { + log.error("트렌드 키워드 추출 실패", e); + return StepExecutionResultDTO.failure(e.getMessage()); + } + } + private String waitForResult(Long executionId, int timeoutSeconds) { + for (int i = 0; i < timeoutSeconds; i++) { + String keyword = blogPublishMapper.findBlogPublishByExecutionId(executionId); + if (keyword != null) { + return keyword; + } + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + break; + } + } + return null; + } +} diff --git a/springboot/src/main/java/com/softlabs/aicontents/scheduler/service/KeywordExecutor.java b/springboot/src/main/java/com/softlabs/aicontents/scheduler/service/KeywordExecutor.java new file mode 100644 index 00000000..caa6db01 --- /dev/null +++ b/springboot/src/main/java/com/softlabs/aicontents/scheduler/service/KeywordExecutor.java @@ -0,0 +1,64 @@ +package com.softlabs.aicontents.scheduler.service; + + +import com.softlabs.aicontents.scheduler.dto.pipeLineDTO.StepExecutionResultDTO; +import com.softlabs.aicontents.scheduler.interfacePipe.PipelineStepExecutor; +import com.softlabs.aicontents.domain.testMapper.KeywordMapper; +import com.softlabs.aicontents.domain.testService.KeywordService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; + + +@Component +@Slf4j +@Service +public class KeywordExecutor implements PipelineStepExecutor { + + @Autowired + private KeywordService keywordService; // 실제 기능 서비스 + + @Autowired + private KeywordMapper keywordMapper; // DB 조회용 + + @Override + public StepExecutionResultDTO execute(Long executionId) { + + try { + // 🎬 1. 서비스 실행 + log.info("🚀 트렌드 키워드 추출 실행 시작"); + keywordService.extractTrendKeyword(executionId); + + // 🔍 2. DB 조회로 결과 확인 (최대 30초 대기) + String keyword = waitForResult(executionId, 30); + + if (keyword != null) { + log.info("✅ 트렌드 키워드 추출 완료: {}", keyword); + return StepExecutionResultDTO.success(keyword); + } else { + return StepExecutionResultDTO.failure("트렌드 키워드 추출 시간 초과"); + } + + } catch (Exception e) { + log.error("❌ 트렌드 키워드 추출 실패", e); + return StepExecutionResultDTO.failure(e.getMessage()); + } + } + + private String waitForResult(Long executionId, int timeoutSeconds) { + for (int i = 0; i < timeoutSeconds; i++) { + String keyword = keywordMapper.findKeywordByExecutionId(executionId); + if (keyword != null) { + return keyword; + } + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + break; + } + } + return null; + } +} diff --git a/springboot/src/main/java/com/softlabs/aicontents/scheduler/service/ProductCrawlingExecutor.java b/springboot/src/main/java/com/softlabs/aicontents/scheduler/service/ProductCrawlingExecutor.java new file mode 100644 index 00000000..86efb4d6 --- /dev/null +++ b/springboot/src/main/java/com/softlabs/aicontents/scheduler/service/ProductCrawlingExecutor.java @@ -0,0 +1,68 @@ +package com.softlabs.aicontents.scheduler.service; + + +import com.softlabs.aicontents.scheduler.dto.pipeLineDTO.StepExecutionResultDTO; +import com.softlabs.aicontents.scheduler.interfacePipe.PipelineStepExecutor; +import com.softlabs.aicontents.domain.testMapper.ProductCrawlingMapper; +import com.softlabs.aicontents.domain.testService.ProductCrawlingService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; + +@Component +@Slf4j +@Service +public class ProductCrawlingExecutor implements PipelineStepExecutor { + + @Autowired + private ProductCrawlingService productCrawlingService; + // todo: 실제 키워드 수집 서비스 클래스로 변경 + + @Autowired + private ProductCrawlingMapper productCrawlingMapper; + // todo: 실제 키워드 매퍼 인터페이스로 변경 + + + @Override + public StepExecutionResultDTO execute(Long executionId) { + //execute02 실행 + + try { + //키워드 수집 서비스 실행 + log.info("트랜드 키워드 추출 메서스 시작"); + productCrawlingService.extractproductCrawling(executionId); + // todo: 실제 키워드 수집 서비스 의 추출 메서드 + + //DB 조회로 결과 확인 (30초 대기 적용) + String keyword = waitForResult(executionId, 30); + + if (keyword != null) { + log.info("✅ 트렌드 키워드 추출 완료: {}", keyword); + return StepExecutionResultDTO.success(keyword); + + } else { + return StepExecutionResultDTO.failure("트렌드 키워드 추출 시간 초과"); + } + + } catch (Exception e) { + log.error("트렌드 키워드 추출 실패", e); + return StepExecutionResultDTO.failure(e.getMessage()); + } + } + private String waitForResult(Long executionId, int timeoutSeconds) { + for (int i = 0; i < timeoutSeconds; i++) { + String keyword = productCrawlingMapper.findproductCrawlingByExecutionId(executionId); + if (keyword != null) { + return keyword; + } + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + break; + } + } + return null; + } +} diff --git a/springboot/src/main/java/com/softlabs/aicontents/scheduler/service/SchedulerService.java b/springboot/src/main/java/com/softlabs/aicontents/scheduler/service/SchedulerService.java deleted file mode 100644 index f9cae313..00000000 --- a/springboot/src/main/java/com/softlabs/aicontents/scheduler/service/SchedulerService.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.softlabs.aicontents.scheduler.service; - - -import com.softlabs.aicontents.scheduler.mapper.SchedulerMapper; -import com.softlabs.aicontents.scheduler.vo.ScheduledResponseVO; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; - -@Service -@RequiredArgsConstructor -//Lombok 어노테이션 -public class SchedulerService { - - private final SchedulerMapper scheduledTaskMapper; - - public int getTaskCount() { - //반환은 int , 메서드 선언 - int res = scheduledTaskMapper.countTasks(); - return res; - } - public ScheduledResponseVO getTask(Long taskId) { - return scheduledTaskMapper.selectByTaskId(taskId); - } - - -} diff --git a/springboot/src/main/java/com/softlabs/aicontents/scheduler/vo/PipelineExecutionRequestVO.java b/springboot/src/main/java/com/softlabs/aicontents/scheduler/vo/PipelineExecutionRequestVO.java deleted file mode 100644 index f019d97a..00000000 --- a/springboot/src/main/java/com/softlabs/aicontents/scheduler/vo/PipelineExecutionRequestVO.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.softlabs.aicontents.scheduler.vo; - -import java.math.BigDecimal; -import java.time.LocalDateTime; - - -//자바 - DB 데이터 송신 객체 - -public record PipelineExecutionRequestVO( - - Long executionId, - Long taskId, - String executionStatus, - String executionMode, - String currentStepCode, - Integer totalSteps, - Integer completedSteps, - BigDecimal progressPercentage, - LocalDateTime scheduledTime, - LocalDateTime startTime, - LocalDateTime endTime, - Long durationMs, - Integer retryCount, - Integer maxRetries, - String errorStep, - String errorMessage, - String errorCode, - String executionResult, - String performanceMetrics -) { - // 새 실행 생성용 (정적)팩토리 메서드 패턴 - // 각 칼럼들의 타입을 미리 작성해 두어 새로 생성할 때 오류 방지 - public static PipelineExecutionRequestVO createNew(Long taskId,String executionMode){ - //메서드 이름은 creatNew - return new PipelineExecutionRequestVO( - null, // executionId (DB 자동 생성) - taskId, - "PENDING", - executionMode, - "STEP_01", - 4, - 0, - BigDecimal.ZERO, - LocalDateTime.now(), - null, - null, - null, - 0, - 3, - null, - null, - null, - null, - null - ); - - - } - - -} - diff --git a/springboot/src/main/java/com/softlabs/aicontents/scheduler/vo/PipelineExecutionResponseVO.java b/springboot/src/main/java/com/softlabs/aicontents/scheduler/vo/PipelineExecutionResponseVO.java deleted file mode 100644 index c9214604..00000000 --- a/springboot/src/main/java/com/softlabs/aicontents/scheduler/vo/PipelineExecutionResponseVO.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.softlabs.aicontents.scheduler.vo; - - -//PIPELINE_EXECUTIONS 테이블 조회용 - -import java.math.BigDecimal; -import java.time.LocalDateTime; - -public record PipelineExecutionResponseVO( - - Long executionId, - Long taskId, - String executionStatus, // PENDING, RUNNING, COMPLETED, FAILED - String executionMode, // SCHEDULED, MANUAL - String currentStepCode, // STEP_01, STEP_02, STEP_03, STEP_04 - Integer totalSteps, - Integer completedSteps, - BigDecimal progressPercentage, - LocalDateTime scheduledTime, - LocalDateTime startTime, - LocalDateTime endTime, - Long durationMs, - Integer retryCount, - Integer maxRetries, - String errorStep, - String errorMessage, - String errorCode, - String executionResult, - String performanceMetrics - - -) { -} diff --git a/springboot/src/main/java/com/softlabs/aicontents/scheduler/vo/ScheduledRequestVO.java b/springboot/src/main/java/com/softlabs/aicontents/scheduler/vo/ScheduledRequestVO.java deleted file mode 100644 index ae85c06b..00000000 --- a/springboot/src/main/java/com/softlabs/aicontents/scheduler/vo/ScheduledRequestVO.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.softlabs.aicontents.scheduler.vo; - -public record ScheduledRequestVO( - String taskName, - String taskDescription, - String cronExpression, - String taskType, - String isActive, - Integer maxRetryCount, - Integer timeoutMinutes - // ID, 생성일시 등 DB 자동생성 필드 제외 -) {} - diff --git a/springboot/src/main/java/com/softlabs/aicontents/scheduler/vo/ScheduledResponseVO.java b/springboot/src/main/java/com/softlabs/aicontents/scheduler/vo/ScheduledResponseVO.java deleted file mode 100644 index a5240bc4..00000000 --- a/springboot/src/main/java/com/softlabs/aicontents/scheduler/vo/ScheduledResponseVO.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.softlabs.aicontents.scheduler.vo; - -import java.time.LocalDateTime; - -public record ScheduledResponseVO( - Long taskId, - String taskName, - String taskDescription, - String cronExpression, - String taskType, - String isActive, - LocalDateTime createdAt, - LocalDateTime updatedAt - // DB에서 조회한 모든 필드 포함 - - ) {} \ No newline at end of file diff --git a/springboot/src/main/resources/mappers/domain/example/HealthMapper.xml b/springboot/src/main/resources/mappers/domain/example/HealthMapper.xml index f08fb5c1..59bca23b 100644 --- a/springboot/src/main/resources/mappers/domain/example/HealthMapper.xml +++ b/springboot/src/main/resources/mappers/domain/example/HealthMapper.xml @@ -1,13 +1,13 @@ - - + + + + - + - - + + + + - + From 9c0dfb29480f57bc9fab06af1b32ea53b642f1fe Mon Sep 17 00:00:00 2001 From: "skykim5538@gmail.com" Date: Wed, 10 Sep 2025 14:26:51 +0900 Subject: [PATCH 06/20] =?UTF-8?q?refactor=20:=20=EC=8A=A4=EC=BC=80?= =?UTF-8?q?=EC=A4=84=EB=9F=AC=20=ED=8C=8C=EC=9D=B4=ED=94=84=EB=9D=BC?= =?UTF-8?q?=EC=9D=B8=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=ED=99=98=EA=B2=BD=20?= =?UTF-8?q?=EA=B5=AC=EC=84=B1=20=EB=B0=8F=20todo=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 테스트용 매퍼 인터페이스/클래스들 주석 처리 (실제 매퍼로 교체 예정) - ScheduleEngine 클래스 추가로 자동 스케줄링 기능 구현 - 파이프라인 각 단계별 테스트용 딜레이 로직 추가 --- .../domain/testMapper/AIContentMapper.java | 24 ++-- .../domain/testMapper/BlogPublishMapper.java | 24 ++-- .../domain/testMapper/KeywordMapper.java | 22 ++-- .../testMapper/ProductCrawlingMapper.java | 26 ++-- .../controller/TestExampleController.java | 28 ----- .../testexample/entity/TestExample.java | 28 ----- .../testexample/mapper/TestExampleMapper.java | 9 -- .../service/TestExampleService.java | 20 ---- .../scheduler/controller/PipelineService.java | 2 +- .../scheduler/controller/ScheduleEngine.java | 43 +++++++ .../pipeLineDTO/StepExecutionResultDTO.java | 32 ++--- .../scheduler/service/AIContentExecutor.java | 112 +++++++++++------ .../service/BlogPublishExecutor.java | 109 +++++++++++------ .../scheduler/service/KeywordExecutor.java | 106 ++++++++++------ .../service/ProductCrawlingExecutor.java | 113 ++++++++++++------ .../mappers/domain/example/HealthMapper.xml | 20 ++-- .../domain/testexample/TestExampleMapper.xml | 14 +-- .../mappers/scheduler/ScheduledTaskMapper.xml | 40 +++---- 18 files changed, 438 insertions(+), 334 deletions(-) delete mode 100644 springboot/src/main/java/com/softlabs/aicontents/domain/testexample/controller/TestExampleController.java delete mode 100644 springboot/src/main/java/com/softlabs/aicontents/domain/testexample/entity/TestExample.java delete mode 100644 springboot/src/main/java/com/softlabs/aicontents/domain/testexample/mapper/TestExampleMapper.java delete mode 100644 springboot/src/main/java/com/softlabs/aicontents/domain/testexample/service/TestExampleService.java create mode 100644 springboot/src/main/java/com/softlabs/aicontents/scheduler/controller/ScheduleEngine.java diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/testMapper/AIContentMapper.java b/springboot/src/main/java/com/softlabs/aicontents/domain/testMapper/AIContentMapper.java index b8f85d00..1b754c27 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/domain/testMapper/AIContentMapper.java +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/testMapper/AIContentMapper.java @@ -1,12 +1,12 @@ -package com.softlabs.aicontents.domain.testMapper; - -import org.apache.ibatis.annotations.Mapper; - -@Mapper -public interface AIContentMapper { - - String findAicontentByExecutionId(Long executionId); - -} - -/// todo : 실제 개발한 Mapper 적용 \ No newline at end of file +//package com.softlabs.aicontents.domain.testMapper; +// +//import org.apache.ibatis.annotations.Mapper; +// +//@Mapper +//public interface AIContentMapper { +// +// String findAicontentByExecutionId(Long executionId); +// +//} +// +///// todo : 실제 개발한 Mapper 적용 \ No newline at end of file diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/testMapper/BlogPublishMapper.java b/springboot/src/main/java/com/softlabs/aicontents/domain/testMapper/BlogPublishMapper.java index fdc6b2ac..e04bf7ba 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/domain/testMapper/BlogPublishMapper.java +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/testMapper/BlogPublishMapper.java @@ -1,12 +1,12 @@ -package com.softlabs.aicontents.domain.testMapper; - - -import org.apache.ibatis.annotations.Mapper; - -@Mapper -public interface BlogPublishMapper { - String findBlogPublishByExecutionId(Long executionId); -} - - -/// todo : 실제 개발한 Mapper 적용 \ No newline at end of file +//package com.softlabs.aicontents.domain.testMapper; +// +// +//import org.apache.ibatis.annotations.Mapper; +// +//@Mapper +//public interface BlogPublishMapper { +// String findBlogPublishByExecutionId(Long executionId); +//} +// +// +///// todo : 실제 개발한 Mapper 적용 \ No newline at end of file diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/testMapper/KeywordMapper.java b/springboot/src/main/java/com/softlabs/aicontents/domain/testMapper/KeywordMapper.java index 5fcb564f..c53d8d96 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/domain/testMapper/KeywordMapper.java +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/testMapper/KeywordMapper.java @@ -1,11 +1,11 @@ -package com.softlabs.aicontents.domain.testMapper; - -import org.apache.ibatis.annotations.Mapper; - -@Mapper -public interface KeywordMapper { - String findKeywordByExecutionId(Long executionId); -} - - -/// todo : 실제 개발한 Mapper 적용 \ No newline at end of file +//package com.softlabs.aicontents.domain.testMapper; +// +//import org.apache.ibatis.annotations.Mapper; +// +//@Mapper +//public interface KeywordMapper { +// String findKeywordByExecutionId(Long executionId); +//} +// +// +///// todo : 실제 개발한 Mapper 적용 \ No newline at end of file diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/testMapper/ProductCrawlingMapper.java b/springboot/src/main/java/com/softlabs/aicontents/domain/testMapper/ProductCrawlingMapper.java index a84c20c9..7548d2fc 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/domain/testMapper/ProductCrawlingMapper.java +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/testMapper/ProductCrawlingMapper.java @@ -1,13 +1,13 @@ -package com.softlabs.aicontents.domain.testMapper; - -import org.apache.ibatis.annotations.Mapper; - -@Mapper -public interface ProductCrawlingMapper { - String findproductCrawlingByExecutionId(Long executionId); - - } - - - -/// todo : 실제 개발한 Mapper 적용 \ No newline at end of file +//package com.softlabs.aicontents.domain.testMapper; +// +//import org.apache.ibatis.annotations.Mapper; +// +//@Mapper +//public interface ProductCrawlingMapper { +// String findproductCrawlingByExecutionId(Long executionId); +// +// } +// +// +// +///// todo : 실제 개발한 Mapper 적용 \ No newline at end of file diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/testexample/controller/TestExampleController.java b/springboot/src/main/java/com/softlabs/aicontents/domain/testexample/controller/TestExampleController.java deleted file mode 100644 index 8a126be7..00000000 --- a/springboot/src/main/java/com/softlabs/aicontents/domain/testexample/controller/TestExampleController.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.softlabs.aicontents.domain.testexample.controller; - -import com.softlabs.aicontents.domain.testexample.service.TestExampleService; -import io.swagger.v3.oas.annotations.Operation; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; - -@RestController -@RequestMapping("/api/test-example") -public class TestExampleController { - - private final TestExampleService testExampleService; - - public TestExampleController(TestExampleService testExampleService) { - this.testExampleService = testExampleService; - } - - @PostMapping - @Operation(summary = "테스트 데이터 삽입", description = "RDS 테이블에 테스트 데이터를 삽입합니다.") - public ResponseEntity createTestExample(@RequestParam String testData) { - try { - testExampleService.createTestExample(testData); - return ResponseEntity.ok("테스트 데이터가 성공적으로 삽입되었습니다: " + testData); - } catch (Exception e) { - return ResponseEntity.status(500).body("데이터 삽입 실패: " + e.getMessage()); - } - } -} diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/testexample/entity/TestExample.java b/springboot/src/main/java/com/softlabs/aicontents/domain/testexample/entity/TestExample.java deleted file mode 100644 index 7d6de007..00000000 --- a/springboot/src/main/java/com/softlabs/aicontents/domain/testexample/entity/TestExample.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.softlabs.aicontents.domain.testexample.entity; - -public class TestExample { - private Long id; - private String testData; - - public TestExample() {} - - public TestExample(String testData) { - this.testData = testData; - } - - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public String getTestData() { - return testData; - } - - public void setTestData(String testData) { - this.testData = testData; - } -} diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/testexample/mapper/TestExampleMapper.java b/springboot/src/main/java/com/softlabs/aicontents/domain/testexample/mapper/TestExampleMapper.java deleted file mode 100644 index 5ca7a055..00000000 --- a/springboot/src/main/java/com/softlabs/aicontents/domain/testexample/mapper/TestExampleMapper.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.softlabs.aicontents.domain.testexample.mapper; - -import com.softlabs.aicontents.domain.testexample.entity.TestExample; -import org.apache.ibatis.annotations.Mapper; - -@Mapper -public interface TestExampleMapper { - void insertTestExample(TestExample testExample); -} diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/testexample/service/TestExampleService.java b/springboot/src/main/java/com/softlabs/aicontents/domain/testexample/service/TestExampleService.java deleted file mode 100644 index 521847f5..00000000 --- a/springboot/src/main/java/com/softlabs/aicontents/domain/testexample/service/TestExampleService.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.softlabs.aicontents.domain.testexample.service; - -import com.softlabs.aicontents.domain.testexample.entity.TestExample; -import com.softlabs.aicontents.domain.testexample.mapper.TestExampleMapper; -import org.springframework.stereotype.Service; - -@Service -public class TestExampleService { - - private final TestExampleMapper testExampleMapper; - - public TestExampleService(TestExampleMapper testExampleMapper) { - this.testExampleMapper = testExampleMapper; - } - - public void createTestExample(String testData) { - TestExample testExample = new TestExample(testData); - testExampleMapper.insertTestExample(testExample); - } -} diff --git a/springboot/src/main/java/com/softlabs/aicontents/scheduler/controller/PipelineService.java b/springboot/src/main/java/com/softlabs/aicontents/scheduler/controller/PipelineService.java index 9b8177f9..8b144e9b 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/scheduler/controller/PipelineService.java +++ b/springboot/src/main/java/com/softlabs/aicontents/scheduler/controller/PipelineService.java @@ -56,7 +56,7 @@ public void executionPipline(){ StepExecutionResultDTO step04 = blogExecutor.execute(executionId); //todo : if 추출 실패 시 3회 재시도 및 예외처리 - log.error("파이프라인 성공"); + log.info("파이프라인 성공"); } catch (Exception e){ log.error("파이프라인 실행 실패:{}",e.getMessage()); diff --git a/springboot/src/main/java/com/softlabs/aicontents/scheduler/controller/ScheduleEngine.java b/springboot/src/main/java/com/softlabs/aicontents/scheduler/controller/ScheduleEngine.java new file mode 100644 index 00000000..0c121126 --- /dev/null +++ b/springboot/src/main/java/com/softlabs/aicontents/scheduler/controller/ScheduleEngine.java @@ -0,0 +1,43 @@ +package com.softlabs.aicontents.scheduler.controller; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +@Component +@Slf4j +@EnableScheduling + +public class ScheduleEngine { + + private int executionCount =0; + private final int MAX_executionCount =3; + private boolean isCompleted = false; + + @Autowired + private PipelineService pipelineService; + + //시간에 따른 자동실행 + // 테스트시에만 사용 + // 초 분 시 일 월 요일 + // 0 7 21 * * * +// @Scheduled(cron = "0 58 21 * * *") + @Scheduled(cron = "1/5 * * * * *") + /// 12시 18분 정적 실행 + + /// todo : 1. 파이프라인 3회 실행 후 종료(for문) + /// todo : 2. 파이프라인 3회 재시도 /예외처리 + + + + public void executePipline() { + /// 파이프라인 실행 메서드 호출 + pipelineService.executionPipline(); + + } + +} + + diff --git a/springboot/src/main/java/com/softlabs/aicontents/scheduler/dto/pipeLineDTO/StepExecutionResultDTO.java b/springboot/src/main/java/com/softlabs/aicontents/scheduler/dto/pipeLineDTO/StepExecutionResultDTO.java index 9795e146..6db0a8dc 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/scheduler/dto/pipeLineDTO/StepExecutionResultDTO.java +++ b/springboot/src/main/java/com/softlabs/aicontents/scheduler/dto/pipeLineDTO/StepExecutionResultDTO.java @@ -10,21 +10,21 @@ public class StepExecutionResultDTO { private String resultData; private String errorMessage; - // 생성자를 private으로 막아서 외부에서 new로 생성하는 것을 방지 - private StepExecutionResultDTO(boolean success, String resultData, String errorMessage) { - this.success = success; - this.resultData = resultData; - this.errorMessage = errorMessage; - } - - // 성공 결과를 생성하는 정적 메서드 - public static StepExecutionResultDTO success(String resultData) { - return new StepExecutionResultDTO(true, resultData, null); - } - - // 실패 결과를 생성하는 정적 메서드 - public static StepExecutionResultDTO failure(String errorMessage) { - return new StepExecutionResultDTO(false, null, errorMessage); - } +// // 생성자를 private으로 막아서 외부에서 new로 생성하는 것을 방지 +// private StepExecutionResultDTO(boolean success, String resultData, String errorMessage) { +// this.success = success; +// this.resultData = resultData; +// this.errorMessage = errorMessage; +// } +// +// // 성공 결과를 생성하는 정적 메서드 +// public static StepExecutionResultDTO success(String resultData) { +// return new StepExecutionResultDTO(true, resultData, null); +// } +// +// // 실패 결과를 생성하는 정적 메서드 +// public static StepExecutionResultDTO failure(String errorMessage) { +// return new StepExecutionResultDTO(false, null, errorMessage); +// } } diff --git a/springboot/src/main/java/com/softlabs/aicontents/scheduler/service/AIContentExecutor.java b/springboot/src/main/java/com/softlabs/aicontents/scheduler/service/AIContentExecutor.java index 205b497d..b0d451e7 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/scheduler/service/AIContentExecutor.java +++ b/springboot/src/main/java/com/softlabs/aicontents/scheduler/service/AIContentExecutor.java @@ -3,7 +3,7 @@ import com.softlabs.aicontents.scheduler.dto.pipeLineDTO.StepExecutionResultDTO; import com.softlabs.aicontents.scheduler.interfacePipe.PipelineStepExecutor; -import com.softlabs.aicontents.domain.testMapper.AIContentMapper; +//import com.softlabs.aicontents.domain.testMapper.AIContentMapper; import com.softlabs.aicontents.domain.testService.AIContentService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -19,50 +19,88 @@ public class AIContentExecutor implements PipelineStepExecutor { private AIContentService aiContentService; // todo: 실제 LLM생성 클래스로 변경 - @Autowired - private AIContentMapper aiContentMapper; - // todo: 실제 LLM생성 매퍼 인터페이스로 변경 +// @Autowired +// private AIContentMapper aiContentMapper; +// // todo: 실제 LLM생성 매퍼 인터페이스로 변경 @Override public StepExecutionResultDTO execute(Long executionId) { - //execute02 실행 - - try { - //키워드 수집 서비스 실행 - log.info("LLM생성 메서스 시작"); - aiContentService.extractAiContent(executionId); - // todo: 실제 키워드 수집 서비스 의 추출 메서드 - - //DB 조회로 결과 확인 (30초 대기 적용) - String keyword = waitForResult(executionId, 30); - - if (keyword != null) { - log.info("✅ 트렌드 키워드 추출 완료: {}", keyword); - return StepExecutionResultDTO.success(keyword); - } else { - return StepExecutionResultDTO.failure("트렌드 키워드 추출 시간 초과"); - } + /// test : 파이프라인 동작 테스트 + System.out.println("LLM생성 메서드 호출/ 실행"); + delayWithDots(3); - } catch (Exception e) { - log.error("트렌드 키워드 추출 실패", e); - return StepExecutionResultDTO.failure(e.getMessage()); - } + /// todo : 테스트용 RDS 조회 쿼리 + System.out.println("LLM생성 결과 DB에서 쿼리 조회"); + delayWithDots(3); + System.out.println("LLM생성 결과 DB 완료 확인 로직 실행"); + delayWithDots(3); + System.out.println("LLM생성 수집 상태 판단 -> 완료(success)"); + System.out.println("LLM생성 수집 상태 판단 -> 실패(failure)-> 재시도/예외처리"); + delayWithDots(3); + System.out.println("[스케줄러]가 [LLM] -> [발행] (요청)객체 전달"); + delayWithDots(3); + return null; + /// todo : 반환 값으로 이전 기능이 요구하는 파라메터를 반환하기. } - private String waitForResult(Long executionId, int timeoutSeconds) { - for (int i = 0; i < timeoutSeconds; i++) { - String keyword = aiContentMapper.findAicontentByExecutionId(executionId); - if (keyword != null) { - return keyword; - } - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - break; + + /// 테스트용 딜레이 메서드 + private void delayWithDots(int seconds) { + try { + for (int i = 0; i < seconds; i++) { + Thread.sleep(200); // 1초마다 + System.out.print("."); } + System.out.println(); // 줄바꿈 + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); } - return null; } } + + + + + + + + +// try { +// //키워드 수집 서비스 실행 +// log.info("LLM생성 메서스 시작"); +// aiContentService.extractAiContent(executionId); +// // todo: 실제 키워드 수집 서비스 의 추출 메서드 +// +// //DB 조회로 결과 확인 (30초 대기 적용) +// String keyword = waitForResult(executionId, 30); +// +// if (keyword != null) { +// log.info("✅ 트렌드 키워드 추출 완료: {}", keyword); +// return StepExecutionResultDTO.success(keyword); +// +// } else { +// return StepExecutionResultDTO.failure("트렌드 키워드 추출 시간 초과"); +// } +// +// } catch (Exception e) { +// log.error("트렌드 키워드 추출 실패", e); +// return StepExecutionResultDTO.failure(e.getMessage()); +// } +// } +// private String waitForResult(Long executionId, int timeoutSeconds) { +// for (int i = 0; i < timeoutSeconds; i++) { +// String keyword = aiContentMapper.findAicontentByExecutionId(executionId); +// if (keyword != null) { +// return keyword; +// } +// try { +// Thread.sleep(1000); +// } catch (InterruptedException e) { +// Thread.currentThread().interrupt(); +// break; +// } +// } +// return null; +// } +//} diff --git a/springboot/src/main/java/com/softlabs/aicontents/scheduler/service/BlogPublishExecutor.java b/springboot/src/main/java/com/softlabs/aicontents/scheduler/service/BlogPublishExecutor.java index cc569358..1755c5b4 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/scheduler/service/BlogPublishExecutor.java +++ b/springboot/src/main/java/com/softlabs/aicontents/scheduler/service/BlogPublishExecutor.java @@ -2,7 +2,7 @@ import com.softlabs.aicontents.scheduler.dto.pipeLineDTO.StepExecutionResultDTO; import com.softlabs.aicontents.scheduler.interfacePipe.PipelineStepExecutor; -import com.softlabs.aicontents.domain.testMapper.BlogPublishMapper; +//import com.softlabs.aicontents.domain.testMapper.BlogPublishMapper; import com.softlabs.aicontents.domain.testService.BlogPublishService; import lombok.extern.slf4j.Slf4j; @@ -17,52 +17,85 @@ public class BlogPublishExecutor implements PipelineStepExecutor { @Autowired private BlogPublishService blogPublishService; - // todo: 실제 LLM생성 클래스로 변경 + // todo: 실제 발행 클래스로 변경 - @Autowired - private BlogPublishMapper blogPublishMapper; - // todo: 실제 LLM생성 매퍼 인터페이스로 변경 +// @Autowired +// private BlogPublishMapper blogPublishMapper; +// // todo: 실제 발행 매퍼 인터페이스로 변경 @Override public StepExecutionResultDTO execute(Long executionId) { + /// test : 파이프라인 동작 테스트 + System.out.println("발행 메서드 호출/ 실행"); + delayWithDots(3); - try { - //키워드 수집 서비스 실행 - log.info("LLM생성 메서스 시작"); - blogPublishService.extractBlogPublish(executionId); - // todo: 실제 키워드 수집 서비스 의 추출 메서드 - - //DB 조회로 결과 확인 (30초 대기 적용) - String keyword = waitForResult(executionId, 30); - - if (keyword != null) { - log.info("✅ 트렌드 키워드 추출 완료: {}", keyword); - return StepExecutionResultDTO.success(keyword); - - } else { - return StepExecutionResultDTO.failure("트렌드 키워드 추출 시간 초과"); - } - - } catch (Exception e) { - log.error("트렌드 키워드 추출 실패", e); - return StepExecutionResultDTO.failure(e.getMessage()); - } + /// todo : 테스트용 RDS 조회 쿼리 + System.out.println("발행 결과 DB에서 쿼리 조회"); + delayWithDots(3); + System.out.println("발행 결과 DB 완료 확인 로직 실행"); + delayWithDots(3); + System.out.println("발행 상태 판단 -> 완료(success)"); + System.out.println("발행 상태 판단 -> 실패(failure)-> 재시도/예외처리"); + delayWithDots(3); + System.out.println("[발행] 완료"); + delayWithDots(3); + return null; + /// todo : 반환 값으로 이전 기능이 요구하는 파라메터를 반환하기. } - private String waitForResult(Long executionId, int timeoutSeconds) { - for (int i = 0; i < timeoutSeconds; i++) { - String keyword = blogPublishMapper.findBlogPublishByExecutionId(executionId); - if (keyword != null) { - return keyword; - } - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - break; + + /// 테스트용 딜레이 메서드 + private void delayWithDots(int seconds) { + try { + for (int i = 0; i < seconds; i++) { + Thread.sleep(200); // 1초마다 + System.out.print("."); } + System.out.println(); // 줄바꿈 + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); } - return null; } } + + + +// try { +// //키워드 수집 서비스 실행 +// log.info("LLM생성 메서스 시작"); +// blogPublishService.extractBlogPublish(executionId); +// // todo: 실제 키워드 수집 서비스 의 추출 메서드 +// +// //DB 조회로 결과 확인 (30초 대기 적용) +// String keyword = waitForResult(executionId, 30); +// +// if (keyword != null) { +// log.info("✅ 트렌드 키워드 추출 완료: {}", keyword); +// return StepExecutionResultDTO.success(keyword); +// +// } else { +// return StepExecutionResultDTO.failure("트렌드 키워드 추출 시간 초과"); +// } +// +// } catch (Exception e) { +// log.error("트렌드 키워드 추출 실패", e); +// return StepExecutionResultDTO.failure(e.getMessage()); +// } +// } +// private String waitForResult(Long executionId, int timeoutSeconds) { +// for (int i = 0; i < timeoutSeconds; i++) { +// String keyword = blogPublishMapper.findBlogPublishByExecutionId(executionId); +// if (keyword != null) { +// return keyword; +// } +// try { +// Thread.sleep(1000); +// } catch (InterruptedException e) { +// Thread.currentThread().interrupt(); +// break; +// } +// } +// return null; +// } +//} diff --git a/springboot/src/main/java/com/softlabs/aicontents/scheduler/service/KeywordExecutor.java b/springboot/src/main/java/com/softlabs/aicontents/scheduler/service/KeywordExecutor.java index caa6db01..6deb05f1 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/scheduler/service/KeywordExecutor.java +++ b/springboot/src/main/java/com/softlabs/aicontents/scheduler/service/KeywordExecutor.java @@ -3,7 +3,7 @@ import com.softlabs.aicontents.scheduler.dto.pipeLineDTO.StepExecutionResultDTO; import com.softlabs.aicontents.scheduler.interfacePipe.PipelineStepExecutor; -import com.softlabs.aicontents.domain.testMapper.KeywordMapper; +//import com.softlabs.aicontents.domain.testMapper.KeywordMapper; import com.softlabs.aicontents.domain.testService.KeywordService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -17,48 +17,86 @@ public class KeywordExecutor implements PipelineStepExecutor { @Autowired - private KeywordService keywordService; // 실제 기능 서비스 + private KeywordService keywordService; + ///todo : 실제 키워드 수집 기능 서비스 - @Autowired - private KeywordMapper keywordMapper; // DB 조회용 +// @Autowired +// private KeywordMapper keywordMapper; // DB 조회용 @Override public StepExecutionResultDTO execute(Long executionId) { - try { - // 🎬 1. 서비스 실행 - log.info("🚀 트렌드 키워드 추출 실행 시작"); - keywordService.extractTrendKeyword(executionId); - - // 🔍 2. DB 조회로 결과 확인 (최대 30초 대기) - String keyword = waitForResult(executionId, 30); - - if (keyword != null) { - log.info("✅ 트렌드 키워드 추출 완료: {}", keyword); - return StepExecutionResultDTO.success(keyword); - } else { - return StepExecutionResultDTO.failure("트렌드 키워드 추출 시간 초과"); - } + /// test : 파이프라인 동작 테스트 + System.out.println("키워드 수집 메서드 호출/ 실행"); + delayWithDots(3); - } catch (Exception e) { - log.error("❌ 트렌드 키워드 추출 실패", e); - return StepExecutionResultDTO.failure(e.getMessage()); - } + /// todo : 테스트용 RDS 조회 쿼리 + System.out.println("키워드 수집 결과 DB에서 쿼리 조회"); + delayWithDots(3); + System.out.println("키워드 수집 결과 DB 완료 확인 로직 실행"); + delayWithDots(3); + System.out.println("키워드 수집 수집 상태 판단 -> 완료(success)"); + System.out.println("키워드 수집 수집 상태 판단 -> 실패(failure)-> 재시도/예외처리"); + delayWithDots(3); + System.out.println("[스케줄러]가 [키워드 수집] -> [싸다구 정보 수집] (요청)객체 전달"); + delayWithDots(3); + return null; + /// todo : 반환 값으로 이전 기능이 요구하는 파라메터를 반환하기. } - private String waitForResult(Long executionId, int timeoutSeconds) { - for (int i = 0; i < timeoutSeconds; i++) { - String keyword = keywordMapper.findKeywordByExecutionId(executionId); - if (keyword != null) { - return keyword; - } - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - break; + /// 테스트용 딜레이 메서드 + private void delayWithDots(int seconds) { + try { + for (int i = 0; i < seconds; i++) { + Thread.sleep(200); // 1초마다 + System.out.print("."); } + System.out.println(); // 줄바꿈 + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); } - return null; } } + + + + + /// Todo : 하기 기능 구현 및 구체화 +// try { +// // 🎬 1. 서비스 실행 +// log.info("🚀 트렌드 키워드 추출 실행 시작"); +// keywordService.extractTrendKeyword(executionId); +// +// // 🔍 2. DB 조회로 결과 확인 (최대 30초 대기) +// String keyword = waitForResult(executionId, 30); +// +// if (keyword != null) { +// log.info("✅ 트렌드 키워드 추출 완료: {}", keyword); +// return StepExecutionResultDTO.success(keyword); +// } else { +// return StepExecutionResultDTO.failure("트렌드 키워드 추출 시간 초과"); +// } +// +// } catch (Exception e) { +// log.error("❌ 트렌드 키워드 추출 실패", e); +// return StepExecutionResultDTO.failure(e.getMessage()); +// } +// } +// +// private String waitForResult(Long executionId, int timeoutSeconds) { +// for (int i = 0; i < timeoutSeconds; i++) { +// String keyword = keywordMapper.findKeywordByExecutionId(executionId); +// if (keyword != null) { +// return keyword; +// } +// try { +// Thread.sleep(1000); +// } catch (InterruptedException e) { +// Thread.currentThread().interrupt(); +// break; +// } +// } +// return null; +// } +//} + diff --git a/springboot/src/main/java/com/softlabs/aicontents/scheduler/service/ProductCrawlingExecutor.java b/springboot/src/main/java/com/softlabs/aicontents/scheduler/service/ProductCrawlingExecutor.java index 86efb4d6..74c5286f 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/scheduler/service/ProductCrawlingExecutor.java +++ b/springboot/src/main/java/com/softlabs/aicontents/scheduler/service/ProductCrawlingExecutor.java @@ -3,7 +3,7 @@ import com.softlabs.aicontents.scheduler.dto.pipeLineDTO.StepExecutionResultDTO; import com.softlabs.aicontents.scheduler.interfacePipe.PipelineStepExecutor; -import com.softlabs.aicontents.domain.testMapper.ProductCrawlingMapper; +//import com.softlabs.aicontents.domain.testMapper.ProductCrawlingMapper; import com.softlabs.aicontents.domain.testService.ProductCrawlingService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -17,52 +17,89 @@ public class ProductCrawlingExecutor implements PipelineStepExecutor { @Autowired private ProductCrawlingService productCrawlingService; - // todo: 실제 키워드 수집 서비스 클래스로 변경 + // todo: 실제 싸다구 정보 수집 서비스 클래스로 변경 - @Autowired - private ProductCrawlingMapper productCrawlingMapper; - // todo: 실제 키워드 매퍼 인터페이스로 변경 +// @Autowired +// private ProductCrawlingMapper productCrawlingMapper; +// // todo: 실제 싸다구 정보 수집 매퍼 인터페이스로 변경 @Override public StepExecutionResultDTO execute(Long executionId) { - //execute02 실행 - - try { - //키워드 수집 서비스 실행 - log.info("트랜드 키워드 추출 메서스 시작"); - productCrawlingService.extractproductCrawling(executionId); - // todo: 실제 키워드 수집 서비스 의 추출 메서드 - //DB 조회로 결과 확인 (30초 대기 적용) - String keyword = waitForResult(executionId, 30); + /// test : 파이프라인 동작 테스트 + System.out.println("싸다구 정보 수집 메서드 호출/ 실행"); + delayWithDots(3); - if (keyword != null) { - log.info("✅ 트렌드 키워드 추출 완료: {}", keyword); - return StepExecutionResultDTO.success(keyword); - - } else { - return StepExecutionResultDTO.failure("트렌드 키워드 추출 시간 초과"); - } - - } catch (Exception e) { - log.error("트렌드 키워드 추출 실패", e); - return StepExecutionResultDTO.failure(e.getMessage()); - } + /// todo : 테스트용 RDS 조회 쿼리 + System.out.println("싸다구 정보 수집 결과 DB에서 쿼리 조회"); + delayWithDots(3); + System.out.println("싸다구 정보 수집 결과 DB 완료 확인 로직 실행"); + delayWithDots(3); + System.out.println("싸다구 정보 수집 상태 판단 -> 완료(success)"); + System.out.println("싸다구 정보 수집 상태 판단 -> 실패(failure)-> 재시도/예외처리"); + delayWithDots(3); + System.out.println("[스케줄러]가 [싸다구 정보 수집] -> [LLM] (요청)객체 전달"); + delayWithDots(3); + return null; + /// todo : 반환 값으로 이전 기능이 요구하는 파라메터를 반환하기. } - private String waitForResult(Long executionId, int timeoutSeconds) { - for (int i = 0; i < timeoutSeconds; i++) { - String keyword = productCrawlingMapper.findproductCrawlingByExecutionId(executionId); - if (keyword != null) { - return keyword; - } - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - break; + + /// 테스트용 딜레이 메서드 + private void delayWithDots(int seconds) { + try { + for (int i = 0; i < seconds; i++) { + Thread.sleep(200); // 1초마다 + System.out.print("."); } + System.out.println(); // 줄바꿈 + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); } - return null; } } + + + + +// try { +// //키워드 수집 서비스 실행 +// log.info("트랜드 키워드 추출 메서스 시작"); +// productCrawlingService.extractproductCrawling(executionId); +// // todo: 실제 싸다구 정보 수집 서비스의 추출 메서드 +// +// //DB 조회로 결과 확인 (30초 대기 적용) +// String keyword = waitForResult(executionId, 30); +// +// if (keyword != null) { +// log.info("✅ 트렌드 키워드 추출 완료: {}", keyword); +// return StepExecutionResultDTO.success(keyword); +// +// } else { +// return StepExecutionResultDTO.failure("트렌드 키워드 추출 시간 초과"); +// } +// +// } catch (Exception e) { +// log.error("트렌드 키워드 추출 실패", e); +// return StepExecutionResultDTO.failure(e.getMessage()); +// } +// } + +// +// /// 이런 시간 제한으로 결과 확인은 비추 - 언제 끝나는 지 명확히 알아야 함 +// private String waitForResult(Long executionId, int timeoutSeconds) { +// for (int i = 0; i < timeoutSeconds; i++) { +// String keyword = productCrawlingMapper.findproductCrawlingByExecutionId(executionId); +// if (keyword != null) { +// return keyword; +// } +// try { +// Thread.sleep(1000); +// } catch (InterruptedException e) { +// Thread.currentThread().interrupt(); +// break; +// } +// } +// return null; +// } +//} diff --git a/springboot/src/main/resources/mappers/domain/example/HealthMapper.xml b/springboot/src/main/resources/mappers/domain/example/HealthMapper.xml index 59bca23b..f08fb5c1 100644 --- a/springboot/src/main/resources/mappers/domain/example/HealthMapper.xml +++ b/springboot/src/main/resources/mappers/domain/example/HealthMapper.xml @@ -1,13 +1,13 @@ - - - - + + - + - - - - + + - + diff --git a/springboot/src/main/resources/mappers/domain/testexample/TestExampleMapper.xml b/springboot/src/main/resources/mappers/domain/testexample/TestExampleMapper.xml index 31f07e09..06fe10b3 100644 --- a/springboot/src/main/resources/mappers/domain/testexample/TestExampleMapper.xml +++ b/springboot/src/main/resources/mappers/domain/testexample/TestExampleMapper.xml @@ -5,12 +5,12 @@ - - - SELECT TEST_EXAMPLE_SEQ.NEXTVAL FROM DUAL - - INSERT INTO TEST_EXAMPLE (ID, TEST_DATA) - VALUES (#{id}, #{testData}) - + + + + + + + \ No newline at end of file diff --git a/springboot/src/main/resources/mappers/scheduler/ScheduledTaskMapper.xml b/springboot/src/main/resources/mappers/scheduler/ScheduledTaskMapper.xml index fb51960e..66e6ec45 100644 --- a/springboot/src/main/resources/mappers/scheduler/ScheduledTaskMapper.xml +++ b/springboot/src/main/resources/mappers/scheduler/ScheduledTaskMapper.xml @@ -6,26 +6,26 @@ - - - - - - + + + + + + + + + + + + + + + + + + + + From 2cec318eaa32d41c842b851afcdfe0f9029d2251 Mon Sep 17 00:00:00 2001 From: "skykim5538@gmail.com" Date: Wed, 10 Sep 2025 14:31:53 +0900 Subject: [PATCH 07/20] =?UTF-8?q?refactor=20:=20=EC=8A=A4=EC=BC=80?= =?UTF-8?q?=EC=A4=84=EB=9F=AC=20=ED=8C=8C=EC=9D=B4=ED=94=84=EB=9D=BC?= =?UTF-8?q?=EC=9D=B8=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=ED=99=98=EA=B2=BD=20?= =?UTF-8?q?=EA=B5=AC=EC=84=B1=20=EB=B0=8F=20todo=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?=20=20-=20=ED=85=8C=EC=8A=A4=ED=8A=B8=EC=9A=A9=20=EB=A7=A4?= =?UTF-8?q?=ED=8D=BC=20=EC=9D=B8=ED=84=B0=ED=8E=98=EC=9D=B4=EC=8A=A4/?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=EB=93=A4=20=EC=A3=BC=EC=84=9D=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=20(=EC=8B=A4=EC=A0=9C=20=EB=A7=A4=ED=8D=BC?= =?UTF-8?q?=EB=A1=9C=20=EA=B5=90=EC=B2=B4=20=EC=98=88=EC=A0=95)=20=20=20-?= =?UTF-8?q?=20ScheduleEngine=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=EB=A1=9C=20=EC=9E=90=EB=8F=99=20=EC=8A=A4=EC=BC=80?= =?UTF-8?q?=EC=A4=84=EB=A7=81=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84=20?= =?UTF-8?q?=20=20-=20=ED=8C=8C=EC=9D=B4=ED=94=84=EB=9D=BC=EC=9D=B8=20?= =?UTF-8?q?=EA=B0=81=20=EB=8B=A8=EA=B3=84=EB=B3=84=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=EC=9A=A9=20=EB=94=9C=EB=A0=88=EC=9D=B4=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/testMapper/AIContentMapper.java | 12 +-- .../domain/testMapper/BlogPublishMapper.java | 12 +-- .../domain/testMapper/KeywordMapper.java | 12 +-- .../testMapper/ProductCrawlingMapper.java | 10 +- .../domain/testService/AIContentService.java | 7 +- .../testService/BlogPublishService.java | 8 +- .../domain/testService/KeywordService.java | 7 +- .../testService/ProductCrawlingService.java | 6 +- .../orchestration/enums/PipelineStep.java | 26 +++-- .../scheduler/controller/PipelineService.java | 97 +++++++++---------- .../scheduler/controller/ScheduleEngine.java | 42 ++++---- .../pipeLineDTO/StepExecutionResultDTO.java | 40 ++++---- .../interfacePipe/PipelineStepExecutor.java | 6 +- .../scheduler/service/AIContentExecutor.java | 95 ++++++++---------- .../service/BlogPublishExecutor.java | 79 +++++++-------- .../scheduler/service/KeywordExecutor.java | 80 +++++++-------- .../service/ProductCrawlingExecutor.java | 79 +++++++-------- 17 files changed, 279 insertions(+), 339 deletions(-) diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/testMapper/AIContentMapper.java b/springboot/src/main/java/com/softlabs/aicontents/domain/testMapper/AIContentMapper.java index 1b754c27..5293fdd1 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/domain/testMapper/AIContentMapper.java +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/testMapper/AIContentMapper.java @@ -1,12 +1,12 @@ -//package com.softlabs.aicontents.domain.testMapper; +// package com.softlabs.aicontents.domain.testMapper; // -//import org.apache.ibatis.annotations.Mapper; +// import org.apache.ibatis.annotations.Mapper; // -//@Mapper -//public interface AIContentMapper { +// @Mapper +// public interface AIContentMapper { // // String findAicontentByExecutionId(Long executionId); // -//} +// } // -///// todo : 실제 개발한 Mapper 적용 \ No newline at end of file +///// todo : 실제 개발한 Mapper 적용 diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/testMapper/BlogPublishMapper.java b/springboot/src/main/java/com/softlabs/aicontents/domain/testMapper/BlogPublishMapper.java index e04bf7ba..7d5323d7 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/domain/testMapper/BlogPublishMapper.java +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/testMapper/BlogPublishMapper.java @@ -1,12 +1,12 @@ -//package com.softlabs.aicontents.domain.testMapper; +// package com.softlabs.aicontents.domain.testMapper; // // -//import org.apache.ibatis.annotations.Mapper; +// import org.apache.ibatis.annotations.Mapper; // -//@Mapper -//public interface BlogPublishMapper { +// @Mapper +// public interface BlogPublishMapper { // String findBlogPublishByExecutionId(Long executionId); -//} +// } // // -///// todo : 실제 개발한 Mapper 적용 \ No newline at end of file +///// todo : 실제 개발한 Mapper 적용 diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/testMapper/KeywordMapper.java b/springboot/src/main/java/com/softlabs/aicontents/domain/testMapper/KeywordMapper.java index c53d8d96..26ba43b4 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/domain/testMapper/KeywordMapper.java +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/testMapper/KeywordMapper.java @@ -1,11 +1,11 @@ -//package com.softlabs.aicontents.domain.testMapper; +// package com.softlabs.aicontents.domain.testMapper; // -//import org.apache.ibatis.annotations.Mapper; +// import org.apache.ibatis.annotations.Mapper; // -//@Mapper -//public interface KeywordMapper { +// @Mapper +// public interface KeywordMapper { // String findKeywordByExecutionId(Long executionId); -//} +// } // // -///// todo : 실제 개발한 Mapper 적용 \ No newline at end of file +///// todo : 실제 개발한 Mapper 적용 diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/testMapper/ProductCrawlingMapper.java b/springboot/src/main/java/com/softlabs/aicontents/domain/testMapper/ProductCrawlingMapper.java index 7548d2fc..03ca1b4d 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/domain/testMapper/ProductCrawlingMapper.java +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/testMapper/ProductCrawlingMapper.java @@ -1,13 +1,13 @@ -//package com.softlabs.aicontents.domain.testMapper; +// package com.softlabs.aicontents.domain.testMapper; // -//import org.apache.ibatis.annotations.Mapper; +// import org.apache.ibatis.annotations.Mapper; // -//@Mapper -//public interface ProductCrawlingMapper { +// @Mapper +// public interface ProductCrawlingMapper { // String findproductCrawlingByExecutionId(Long executionId); // // } // // // -///// todo : 실제 개발한 Mapper 적용 \ No newline at end of file +///// todo : 실제 개발한 Mapper 적용 diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/testService/AIContentService.java b/springboot/src/main/java/com/softlabs/aicontents/domain/testService/AIContentService.java index 9713f907..e7e1cfc4 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/domain/testService/AIContentService.java +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/testService/AIContentService.java @@ -1,14 +1,11 @@ package com.softlabs.aicontents.domain.testService; - import org.springframework.stereotype.Service; @Service public class AIContentService { - public void extractAiContent(Long executionId) { - } + public void extractAiContent(Long executionId) {} } - -///todo : 실제 구현 클래스 적용 \ No newline at end of file +/// todo : 실제 구현 클래스 적용 diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/testService/BlogPublishService.java b/springboot/src/main/java/com/softlabs/aicontents/domain/testService/BlogPublishService.java index 08589b5a..fa301945 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/domain/testService/BlogPublishService.java +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/testService/BlogPublishService.java @@ -1,14 +1,10 @@ package com.softlabs.aicontents.domain.testService; - import org.springframework.stereotype.Service; @Service public class BlogPublishService { - public void extractBlogPublish(Long executionId) { - - } + public void extractBlogPublish(Long executionId) {} } - -///todo : 실제 구현 클래스 적용 \ No newline at end of file +/// todo : 실제 구현 클래스 적용 diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/testService/KeywordService.java b/springboot/src/main/java/com/softlabs/aicontents/domain/testService/KeywordService.java index 6bb03731..eaf43808 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/domain/testService/KeywordService.java +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/testService/KeywordService.java @@ -4,10 +4,7 @@ @Service public class KeywordService { - public void extractTrendKeyword(Long executionId) { - - } + public void extractTrendKeyword(Long executionId) {} } - -///todo : 실제 구현 클래스 적용 \ No newline at end of file +/// todo : 실제 구현 클래스 적용 diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/testService/ProductCrawlingService.java b/springboot/src/main/java/com/softlabs/aicontents/domain/testService/ProductCrawlingService.java index 47364f13..d9be2ac2 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/domain/testService/ProductCrawlingService.java +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/testService/ProductCrawlingService.java @@ -4,9 +4,7 @@ @Service public class ProductCrawlingService { - public void extractproductCrawling(Long executionId) { - } + public void extractproductCrawling(Long executionId) {} } - -///todo : 실제 구현 클래스 적용 \ No newline at end of file +/// todo : 실제 구현 클래스 적용 diff --git a/springboot/src/main/java/com/softlabs/aicontents/orchestration/enums/PipelineStep.java b/springboot/src/main/java/com/softlabs/aicontents/orchestration/enums/PipelineStep.java index 80a9edb7..45496d04 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/orchestration/enums/PipelineStep.java +++ b/springboot/src/main/java/com/softlabs/aicontents/orchestration/enums/PipelineStep.java @@ -1,20 +1,18 @@ package com.softlabs.aicontents.orchestration.enums; public enum PipelineStep { - STEP_01("STEP_01", "구글 트렌드 키워드 수집", 1), - STEP_02("STEP_02", "싸다구몰 상품 수집", 2), - STEP_03("STEP_03", "AI 콘텐츠 생성", 3), - STEP_04("STEP_04", "네이버 블로그 업로드", 4); - - private final String code; - private final String description; - private final int order; - - PipelineStep(String code, String description, int order) { - this.code = code; - this.description = description; - this.order = order; - } + STEP_01("STEP_01", "구글 트렌드 키워드 수집", 1), + STEP_02("STEP_02", "싸다구몰 상품 수집", 2), + STEP_03("STEP_03", "AI 콘텐츠 생성", 3), + STEP_04("STEP_04", "네이버 블로그 업로드", 4); + private final String code; + private final String description; + private final int order; + PipelineStep(String code, String description, int order) { + this.code = code; + this.description = description; + this.order = order; + } } diff --git a/springboot/src/main/java/com/softlabs/aicontents/scheduler/controller/PipelineService.java b/springboot/src/main/java/com/softlabs/aicontents/scheduler/controller/PipelineService.java index 8b144e9b..97c0d4f2 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/scheduler/controller/PipelineService.java +++ b/springboot/src/main/java/com/softlabs/aicontents/scheduler/controller/PipelineService.java @@ -2,9 +2,9 @@ import com.softlabs.aicontents.scheduler.dto.pipeLineDTO.StepExecutionResultDTO; import com.softlabs.aicontents.scheduler.service.AIContentExecutor; -import com.softlabs.aicontents.scheduler.service.ProductCrawlingExecutor; -import com.softlabs.aicontents.scheduler.service.KeywordExecutor; import com.softlabs.aicontents.scheduler.service.BlogPublishExecutor; +import com.softlabs.aicontents.scheduler.service.KeywordExecutor; +import com.softlabs.aicontents.scheduler.service.ProductCrawlingExecutor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -15,69 +15,60 @@ @Component public class PipelineService { + // 🎯 실행 인터페이스들만 주입 + @Autowired private KeywordExecutor keywordExecutor; - // 🎯 실행 인터페이스들만 주입 - @Autowired - private KeywordExecutor keywordExecutor; - - @Autowired - private ProductCrawlingExecutor crawlingExecutor; - - @Autowired - private AIContentExecutor aiExecutor; + @Autowired private ProductCrawlingExecutor crawlingExecutor; - @Autowired - private BlogPublishExecutor blogExecutor; + @Autowired private AIContentExecutor aiExecutor; - public void executionPipline(){ - Long executionId = createNewExecution(); - //todo : executionId = (동일한 파이프라인인지 구분하는 용도) - //DB 에서 PIPELINE_EXECUTIONS 테이블의 execution_id + @Autowired private BlogPublishExecutor blogExecutor; - try { /// 파이프라인 전체 try-catch - // 각 단계를 순차적으로 실행 (실행과 검증이 포함되어 있음) + public void executionPipline() { + Long executionId = createNewExecution(); + // todo : executionId = (동일한 파이프라인인지 구분하는 용도) + // DB 에서 PIPELINE_EXECUTIONS 테이블의 execution_id - //step01 - 키워드 추출 - StepExecutionResultDTO step01 = keywordExecutor.execute(executionId); - //todo : if 추출 실패 시 3회 재시도 및 예외처리 - // 예시 : - // if (!step1.isSuccess()) { - // throw new RuntimeException("1단계 실패: " + step1.getErrorMessage()); + try { /// 파이프라인 전체 try-catch + // 각 단계를 순차적으로 실행 (실행과 검증이 포함되어 있음) - //step02 - 상품정보 & URL 추출 - StepExecutionResultDTO step02 = crawlingExecutor.execute(executionId); - //todo : if 추출 실패 시 3회 재시도 및 예외처리 + // step01 - 키워드 추출 + StepExecutionResultDTO step01 = keywordExecutor.execute(executionId); + // todo : if 추출 실패 시 3회 재시도 및 예외처리 + // 예시 : + // if (!step1.isSuccess()) { + // throw new RuntimeException("1단계 실패: " + step1.getErrorMessage()); - //step03 - LLM 생성 - StepExecutionResultDTO step03 = aiExecutor.execute(executionId); - //todo : if 추출 실패 시 3회 재시도 및 예외처리 + // step02 - 상품정보 & URL 추출 + StepExecutionResultDTO step02 = crawlingExecutor.execute(executionId); + // todo : if 추출 실패 시 3회 재시도 및 예외처리 - //step04 - 블로그 발행 - StepExecutionResultDTO step04 = blogExecutor.execute(executionId); - //todo : if 추출 실패 시 3회 재시도 및 예외처리 + // step03 - LLM 생성 + StepExecutionResultDTO step03 = aiExecutor.execute(executionId); + // todo : if 추출 실패 시 3회 재시도 및 예외처리 - log.info("파이프라인 성공"); + // step04 - 블로그 발행 + StepExecutionResultDTO step04 = blogExecutor.execute(executionId); + // todo : if 추출 실패 시 3회 재시도 및 예외처리 - } catch (Exception e){ - log.error("파이프라인 실행 실패:{}",e.getMessage()); - updateExecutionStatus(executionId, "FAILED"); - } + log.info("파이프라인 성공"); - } - - private Long createNewExecution() { - - return 0L; ///일단은 Long타입의 기본값. - //todo: return 반환값으로, - // PIPELINE_EXECUTIONS 테이블에서 executionId를 새로 생성하고, - // 이것을 가져오는 메서드 구현 - // => 파이프라인이 새로 실행될 때마다 executionId를 생성 - } - - private void updateExecutionStatus(Long executionId, String failed) { - //todo: PIPELINE_EXECUTIONS에 상태 업데이트하는 코드 구현(SUCCESS, FAILED,PENDING 등등등) + } catch (Exception e) { + log.error("파이프라인 실행 실패:{}", e.getMessage()); + updateExecutionStatus(executionId, "FAILED"); } -} + } + private Long createNewExecution() { + return 0L; /// 일단은 Long타입의 기본값. + // todo: return 반환값으로, + // PIPELINE_EXECUTIONS 테이블에서 executionId를 새로 생성하고, + // 이것을 가져오는 메서드 구현 + // => 파이프라인이 새로 실행될 때마다 executionId를 생성 + } + private void updateExecutionStatus(Long executionId, String failed) { + // todo: PIPELINE_EXECUTIONS에 상태 업데이트하는 코드 구현(SUCCESS, FAILED,PENDING 등등등) + } +} diff --git a/springboot/src/main/java/com/softlabs/aicontents/scheduler/controller/ScheduleEngine.java b/springboot/src/main/java/com/softlabs/aicontents/scheduler/controller/ScheduleEngine.java index 0c121126..b0462d27 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/scheduler/controller/ScheduleEngine.java +++ b/springboot/src/main/java/com/softlabs/aicontents/scheduler/controller/ScheduleEngine.java @@ -9,35 +9,27 @@ @Component @Slf4j @EnableScheduling - public class ScheduleEngine { - private int executionCount =0; - private final int MAX_executionCount =3; - private boolean isCompleted = false; - - @Autowired - private PipelineService pipelineService; - - //시간에 따른 자동실행 - // 테스트시에만 사용 - // 초 분 시 일 월 요일 - // 0 7 21 * * * -// @Scheduled(cron = "0 58 21 * * *") - @Scheduled(cron = "1/5 * * * * *") - /// 12시 18분 정적 실행 + private int executionCount = 0; + private final int MAX_executionCount = 3; + private boolean isCompleted = false; - /// todo : 1. 파이프라인 3회 실행 후 종료(for문) - /// todo : 2. 파이프라인 3회 재시도 /예외처리 + @Autowired private PipelineService pipelineService; + // 시간에 따른 자동실행 + // 테스트시에만 사용 + // 초 분 시 일 월 요일 + // 0 7 21 * * * + // @Scheduled(cron = "0 58 21 * * *") + @Scheduled(cron = "1/5 * * * * *") + /// 12시 18분 정적 실행 + /// todo : 1. 파이프라인 3회 실행 후 종료(for문) + /// todo : 2. 파이프라인 3회 재시도 /예외처리 - public void executePipline() { - /// 파이프라인 실행 메서드 호출 - pipelineService.executionPipline(); - - } - + public void executePipline() { + /// 파이프라인 실행 메서드 호출 + pipelineService.executionPipline(); + } } - - diff --git a/springboot/src/main/java/com/softlabs/aicontents/scheduler/dto/pipeLineDTO/StepExecutionResultDTO.java b/springboot/src/main/java/com/softlabs/aicontents/scheduler/dto/pipeLineDTO/StepExecutionResultDTO.java index 6db0a8dc..c0f8ec4a 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/scheduler/dto/pipeLineDTO/StepExecutionResultDTO.java +++ b/springboot/src/main/java/com/softlabs/aicontents/scheduler/dto/pipeLineDTO/StepExecutionResultDTO.java @@ -1,30 +1,28 @@ package com.softlabs.aicontents.scheduler.dto.pipeLineDTO; - import lombok.Getter; @Getter public class StepExecutionResultDTO { - private boolean success; - private String resultData; - private String errorMessage; + private boolean success; + private String resultData; + private String errorMessage; -// // 생성자를 private으로 막아서 외부에서 new로 생성하는 것을 방지 -// private StepExecutionResultDTO(boolean success, String resultData, String errorMessage) { -// this.success = success; -// this.resultData = resultData; -// this.errorMessage = errorMessage; -// } -// -// // 성공 결과를 생성하는 정적 메서드 -// public static StepExecutionResultDTO success(String resultData) { -// return new StepExecutionResultDTO(true, resultData, null); -// } -// -// // 실패 결과를 생성하는 정적 메서드 -// public static StepExecutionResultDTO failure(String errorMessage) { -// return new StepExecutionResultDTO(false, null, errorMessage); -// } + // // 생성자를 private으로 막아서 외부에서 new로 생성하는 것을 방지 + // private StepExecutionResultDTO(boolean success, String resultData, String errorMessage) { + // this.success = success; + // this.resultData = resultData; + // this.errorMessage = errorMessage; + // } + // + // // 성공 결과를 생성하는 정적 메서드 + // public static StepExecutionResultDTO success(String resultData) { + // return new StepExecutionResultDTO(true, resultData, null); + // } + // + // // 실패 결과를 생성하는 정적 메서드 + // public static StepExecutionResultDTO failure(String errorMessage) { + // return new StepExecutionResultDTO(false, null, errorMessage); + // } } - diff --git a/springboot/src/main/java/com/softlabs/aicontents/scheduler/interfacePipe/PipelineStepExecutor.java b/springboot/src/main/java/com/softlabs/aicontents/scheduler/interfacePipe/PipelineStepExecutor.java index fd754353..9181be05 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/scheduler/interfacePipe/PipelineStepExecutor.java +++ b/springboot/src/main/java/com/softlabs/aicontents/scheduler/interfacePipe/PipelineStepExecutor.java @@ -3,9 +3,7 @@ import com.softlabs.aicontents.scheduler.dto.pipeLineDTO.StepExecutionResultDTO; public interface PipelineStepExecutor { -// 파이프라인 실행관련 공통 인터페이스 - - StepExecutionResultDTO execute(Long executionId); + // 파이프라인 실행관련 공통 인터페이스 + StepExecutionResultDTO execute(Long executionId); } - diff --git a/springboot/src/main/java/com/softlabs/aicontents/scheduler/service/AIContentExecutor.java b/springboot/src/main/java/com/softlabs/aicontents/scheduler/service/AIContentExecutor.java index b0d451e7..0d34eed0 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/scheduler/service/AIContentExecutor.java +++ b/springboot/src/main/java/com/softlabs/aicontents/scheduler/service/AIContentExecutor.java @@ -1,10 +1,9 @@ package com.softlabs.aicontents.scheduler.service; - +import com.softlabs.aicontents.domain.testService.AIContentService; import com.softlabs.aicontents.scheduler.dto.pipeLineDTO.StepExecutionResultDTO; import com.softlabs.aicontents.scheduler.interfacePipe.PipelineStepExecutor; -//import com.softlabs.aicontents.domain.testMapper.AIContentMapper; -import com.softlabs.aicontents.domain.testService.AIContentService; +// import com.softlabs.aicontents.domain.testMapper.AIContentMapper; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -15,57 +14,49 @@ @Service public class AIContentExecutor implements PipelineStepExecutor { - @Autowired - private AIContentService aiContentService; - // todo: 실제 LLM생성 클래스로 변경 - -// @Autowired -// private AIContentMapper aiContentMapper; -// // todo: 실제 LLM생성 매퍼 인터페이스로 변경 - - - @Override - public StepExecutionResultDTO execute(Long executionId) { - - /// test : 파이프라인 동작 테스트 - System.out.println("LLM생성 메서드 호출/ 실행"); - delayWithDots(3); - - /// todo : 테스트용 RDS 조회 쿼리 - System.out.println("LLM생성 결과 DB에서 쿼리 조회"); - delayWithDots(3); - System.out.println("LLM생성 결과 DB 완료 확인 로직 실행"); - delayWithDots(3); - System.out.println("LLM생성 수집 상태 판단 -> 완료(success)"); - System.out.println("LLM생성 수집 상태 판단 -> 실패(failure)-> 재시도/예외처리"); - delayWithDots(3); - System.out.println("[스케줄러]가 [LLM] -> [발행] (요청)객체 전달"); - delayWithDots(3); - return null; - /// todo : 반환 값으로 이전 기능이 요구하는 파라메터를 반환하기. - } - - /// 테스트용 딜레이 메서드 - private void delayWithDots(int seconds) { - try { - for (int i = 0; i < seconds; i++) { - Thread.sleep(200); // 1초마다 - System.out.print("."); - } - System.out.println(); // 줄바꿈 - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } + @Autowired private AIContentService aiContentService; + + // todo: 실제 LLM생성 클래스로 변경 + + // @Autowired + // private AIContentMapper aiContentMapper; + // // todo: 실제 LLM생성 매퍼 인터페이스로 변경 + + @Override + public StepExecutionResultDTO execute(Long executionId) { + + /// test : 파이프라인 동작 테스트 + System.out.println("LLM생성 메서드 호출/ 실행"); + delayWithDots(3); + + /// todo : 테스트용 RDS 조회 쿼리 + System.out.println("LLM생성 결과 DB에서 쿼리 조회"); + delayWithDots(3); + System.out.println("LLM생성 결과 DB 완료 확인 로직 실행"); + delayWithDots(3); + System.out.println("LLM생성 수집 상태 판단 -> 완료(success)"); + System.out.println("LLM생성 수집 상태 판단 -> 실패(failure)-> 재시도/예외처리"); + delayWithDots(3); + System.out.println("[스케줄러]가 [LLM] -> [발행] (요청)객체 전달"); + delayWithDots(3); + return null; + /// todo : 반환 값으로 이전 기능이 요구하는 파라메터를 반환하기. + } + + /// 테스트용 딜레이 메서드 + private void delayWithDots(int seconds) { + try { + for (int i = 0; i < seconds; i++) { + Thread.sleep(200); // 1초마다 + System.out.print("."); + } + System.out.println(); // 줄바꿈 + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); } + } } - - - - - - - // try { // //키워드 수집 서비스 실행 // log.info("LLM생성 메서스 시작"); @@ -103,4 +94,4 @@ private void delayWithDots(int seconds) { // } // return null; // } -//} +// } diff --git a/springboot/src/main/java/com/softlabs/aicontents/scheduler/service/BlogPublishExecutor.java b/springboot/src/main/java/com/softlabs/aicontents/scheduler/service/BlogPublishExecutor.java index 1755c5b4..cf23fb73 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/scheduler/service/BlogPublishExecutor.java +++ b/springboot/src/main/java/com/softlabs/aicontents/scheduler/service/BlogPublishExecutor.java @@ -1,66 +1,61 @@ package com.softlabs.aicontents.scheduler.service; +import com.softlabs.aicontents.domain.testService.BlogPublishService; import com.softlabs.aicontents.scheduler.dto.pipeLineDTO.StepExecutionResultDTO; import com.softlabs.aicontents.scheduler.interfacePipe.PipelineStepExecutor; -//import com.softlabs.aicontents.domain.testMapper.BlogPublishMapper; -import com.softlabs.aicontents.domain.testService.BlogPublishService; +// import com.softlabs.aicontents.domain.testMapper.BlogPublishMapper; import lombok.extern.slf4j.Slf4j; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.stereotype.Service; - @Component @Slf4j @Service public class BlogPublishExecutor implements PipelineStepExecutor { - @Autowired - private BlogPublishService blogPublishService; - // todo: 실제 발행 클래스로 변경 + @Autowired private BlogPublishService blogPublishService; -// @Autowired -// private BlogPublishMapper blogPublishMapper; -// // todo: 실제 발행 매퍼 인터페이스로 변경 + // todo: 실제 발행 클래스로 변경 + // @Autowired + // private BlogPublishMapper blogPublishMapper; + // // todo: 실제 발행 매퍼 인터페이스로 변경 - @Override - public StepExecutionResultDTO execute(Long executionId) { + @Override + public StepExecutionResultDTO execute(Long executionId) { - /// test : 파이프라인 동작 테스트 - System.out.println("발행 메서드 호출/ 실행"); - delayWithDots(3); + /// test : 파이프라인 동작 테스트 + System.out.println("발행 메서드 호출/ 실행"); + delayWithDots(3); - /// todo : 테스트용 RDS 조회 쿼리 - System.out.println("발행 결과 DB에서 쿼리 조회"); - delayWithDots(3); - System.out.println("발행 결과 DB 완료 확인 로직 실행"); - delayWithDots(3); - System.out.println("발행 상태 판단 -> 완료(success)"); - System.out.println("발행 상태 판단 -> 실패(failure)-> 재시도/예외처리"); - delayWithDots(3); - System.out.println("[발행] 완료"); - delayWithDots(3); - return null; - /// todo : 반환 값으로 이전 기능이 요구하는 파라메터를 반환하기. - } + /// todo : 테스트용 RDS 조회 쿼리 + System.out.println("발행 결과 DB에서 쿼리 조회"); + delayWithDots(3); + System.out.println("발행 결과 DB 완료 확인 로직 실행"); + delayWithDots(3); + System.out.println("발행 상태 판단 -> 완료(success)"); + System.out.println("발행 상태 판단 -> 실패(failure)-> 재시도/예외처리"); + delayWithDots(3); + System.out.println("[발행] 완료"); + delayWithDots(3); + return null; + /// todo : 반환 값으로 이전 기능이 요구하는 파라메터를 반환하기. + } - /// 테스트용 딜레이 메서드 - private void delayWithDots(int seconds) { - try { - for (int i = 0; i < seconds; i++) { - Thread.sleep(200); // 1초마다 - System.out.print("."); - } - System.out.println(); // 줄바꿈 - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } + /// 테스트용 딜레이 메서드 + private void delayWithDots(int seconds) { + try { + for (int i = 0; i < seconds; i++) { + Thread.sleep(200); // 1초마다 + System.out.print("."); + } + System.out.println(); // 줄바꿈 + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); } + } } - - // try { // //키워드 수집 서비스 실행 // log.info("LLM생성 메서스 시작"); @@ -98,4 +93,4 @@ private void delayWithDots(int seconds) { // } // return null; // } -//} +// } diff --git a/springboot/src/main/java/com/softlabs/aicontents/scheduler/service/KeywordExecutor.java b/springboot/src/main/java/com/softlabs/aicontents/scheduler/service/KeywordExecutor.java index 6deb05f1..0b707570 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/scheduler/service/KeywordExecutor.java +++ b/springboot/src/main/java/com/softlabs/aicontents/scheduler/service/KeywordExecutor.java @@ -1,66 +1,61 @@ package com.softlabs.aicontents.scheduler.service; - +import com.softlabs.aicontents.domain.testService.KeywordService; import com.softlabs.aicontents.scheduler.dto.pipeLineDTO.StepExecutionResultDTO; import com.softlabs.aicontents.scheduler.interfacePipe.PipelineStepExecutor; -//import com.softlabs.aicontents.domain.testMapper.KeywordMapper; -import com.softlabs.aicontents.domain.testService.KeywordService; +// import com.softlabs.aicontents.domain.testMapper.KeywordMapper; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.stereotype.Service; - @Component @Slf4j @Service public class KeywordExecutor implements PipelineStepExecutor { - @Autowired - private KeywordService keywordService; - ///todo : 실제 키워드 수집 기능 서비스 + @Autowired private KeywordService keywordService; -// @Autowired -// private KeywordMapper keywordMapper; // DB 조회용 + /// todo : 실제 키워드 수집 기능 서비스 - @Override - public StepExecutionResultDTO execute(Long executionId) { + // @Autowired + // private KeywordMapper keywordMapper; // DB 조회용 - /// test : 파이프라인 동작 테스트 - System.out.println("키워드 수집 메서드 호출/ 실행"); - delayWithDots(3); + @Override + public StepExecutionResultDTO execute(Long executionId) { - /// todo : 테스트용 RDS 조회 쿼리 - System.out.println("키워드 수집 결과 DB에서 쿼리 조회"); - delayWithDots(3); - System.out.println("키워드 수집 결과 DB 완료 확인 로직 실행"); - delayWithDots(3); - System.out.println("키워드 수집 수집 상태 판단 -> 완료(success)"); - System.out.println("키워드 수집 수집 상태 판단 -> 실패(failure)-> 재시도/예외처리"); - delayWithDots(3); - System.out.println("[스케줄러]가 [키워드 수집] -> [싸다구 정보 수집] (요청)객체 전달"); - delayWithDots(3); - return null; - /// todo : 반환 값으로 이전 기능이 요구하는 파라메터를 반환하기. - } + /// test : 파이프라인 동작 테스트 + System.out.println("키워드 수집 메서드 호출/ 실행"); + delayWithDots(3); - /// 테스트용 딜레이 메서드 - private void delayWithDots(int seconds) { - try { - for (int i = 0; i < seconds; i++) { - Thread.sleep(200); // 1초마다 - System.out.print("."); - } - System.out.println(); // 줄바꿈 - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } + /// todo : 테스트용 RDS 조회 쿼리 + System.out.println("키워드 수집 결과 DB에서 쿼리 조회"); + delayWithDots(3); + System.out.println("키워드 수집 결과 DB 완료 확인 로직 실행"); + delayWithDots(3); + System.out.println("키워드 수집 수집 상태 판단 -> 완료(success)"); + System.out.println("키워드 수집 수집 상태 판단 -> 실패(failure)-> 재시도/예외처리"); + delayWithDots(3); + System.out.println("[스케줄러]가 [키워드 수집] -> [싸다구 정보 수집] (요청)객체 전달"); + delayWithDots(3); + return null; + /// todo : 반환 값으로 이전 기능이 요구하는 파라메터를 반환하기. + } + + /// 테스트용 딜레이 메서드 + private void delayWithDots(int seconds) { + try { + for (int i = 0; i < seconds; i++) { + Thread.sleep(200); // 1초마다 + System.out.print("."); + } + System.out.println(); // 줄바꿈 + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); } + } } - - - /// Todo : 하기 기능 구현 및 구체화 // try { // // 🎬 1. 서비스 실행 @@ -98,5 +93,4 @@ private void delayWithDots(int seconds) { // } // return null; // } -//} - +// } diff --git a/springboot/src/main/java/com/softlabs/aicontents/scheduler/service/ProductCrawlingExecutor.java b/springboot/src/main/java/com/softlabs/aicontents/scheduler/service/ProductCrawlingExecutor.java index 74c5286f..33abfd07 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/scheduler/service/ProductCrawlingExecutor.java +++ b/springboot/src/main/java/com/softlabs/aicontents/scheduler/service/ProductCrawlingExecutor.java @@ -1,10 +1,9 @@ package com.softlabs.aicontents.scheduler.service; - +import com.softlabs.aicontents.domain.testService.ProductCrawlingService; import com.softlabs.aicontents.scheduler.dto.pipeLineDTO.StepExecutionResultDTO; import com.softlabs.aicontents.scheduler.interfacePipe.PipelineStepExecutor; -//import com.softlabs.aicontents.domain.testMapper.ProductCrawlingMapper; -import com.softlabs.aicontents.domain.testService.ProductCrawlingService; +// import com.softlabs.aicontents.domain.testMapper.ProductCrawlingMapper; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -15,53 +14,49 @@ @Service public class ProductCrawlingExecutor implements PipelineStepExecutor { - @Autowired - private ProductCrawlingService productCrawlingService; - // todo: 실제 싸다구 정보 수집 서비스 클래스로 변경 + @Autowired private ProductCrawlingService productCrawlingService; -// @Autowired -// private ProductCrawlingMapper productCrawlingMapper; -// // todo: 실제 싸다구 정보 수집 매퍼 인터페이스로 변경 + // todo: 실제 싸다구 정보 수집 서비스 클래스로 변경 + // @Autowired + // private ProductCrawlingMapper productCrawlingMapper; + // // todo: 실제 싸다구 정보 수집 매퍼 인터페이스로 변경 - @Override - public StepExecutionResultDTO execute(Long executionId) { + @Override + public StepExecutionResultDTO execute(Long executionId) { - /// test : 파이프라인 동작 테스트 - System.out.println("싸다구 정보 수집 메서드 호출/ 실행"); - delayWithDots(3); + /// test : 파이프라인 동작 테스트 + System.out.println("싸다구 정보 수집 메서드 호출/ 실행"); + delayWithDots(3); - /// todo : 테스트용 RDS 조회 쿼리 - System.out.println("싸다구 정보 수집 결과 DB에서 쿼리 조회"); - delayWithDots(3); - System.out.println("싸다구 정보 수집 결과 DB 완료 확인 로직 실행"); - delayWithDots(3); - System.out.println("싸다구 정보 수집 상태 판단 -> 완료(success)"); - System.out.println("싸다구 정보 수집 상태 판단 -> 실패(failure)-> 재시도/예외처리"); - delayWithDots(3); - System.out.println("[스케줄러]가 [싸다구 정보 수집] -> [LLM] (요청)객체 전달"); - delayWithDots(3); - return null; - /// todo : 반환 값으로 이전 기능이 요구하는 파라메터를 반환하기. - } + /// todo : 테스트용 RDS 조회 쿼리 + System.out.println("싸다구 정보 수집 결과 DB에서 쿼리 조회"); + delayWithDots(3); + System.out.println("싸다구 정보 수집 결과 DB 완료 확인 로직 실행"); + delayWithDots(3); + System.out.println("싸다구 정보 수집 상태 판단 -> 완료(success)"); + System.out.println("싸다구 정보 수집 상태 판단 -> 실패(failure)-> 재시도/예외처리"); + delayWithDots(3); + System.out.println("[스케줄러]가 [싸다구 정보 수집] -> [LLM] (요청)객체 전달"); + delayWithDots(3); + return null; + /// todo : 반환 값으로 이전 기능이 요구하는 파라메터를 반환하기. + } - /// 테스트용 딜레이 메서드 - private void delayWithDots(int seconds) { - try { - for (int i = 0; i < seconds; i++) { - Thread.sleep(200); // 1초마다 - System.out.print("."); - } - System.out.println(); // 줄바꿈 - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } + /// 테스트용 딜레이 메서드 + private void delayWithDots(int seconds) { + try { + for (int i = 0; i < seconds; i++) { + Thread.sleep(200); // 1초마다 + System.out.print("."); + } + System.out.println(); // 줄바꿈 + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); } + } } - - - // try { // //키워드 수집 서비스 실행 // log.info("트랜드 키워드 추출 메서스 시작"); @@ -102,4 +97,4 @@ private void delayWithDots(int seconds) { // } // return null; // } -//} +// } From 5469d127678718eb0afd40b91817dd48065a2f80 Mon Sep 17 00:00:00 2001 From: "skykim5538@gmail.com" Date: Wed, 10 Sep 2025 16:36:00 +0900 Subject: [PATCH 08/20] =?UTF-8?q?chore:=20feature/common=20=EB=B8=8C?= =?UTF-8?q?=EB=9E=9C=EC=B9=98=EC=9D=98=20=EC=B5=9C=EC=8B=A0=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=EC=82=AC=ED=95=AD=20=EB=B3=91=ED=95=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../orchestration/TestOrchestration.java | 3 + .../orchestration/enums/PipelineStep.java | 2 +- .../domain/scheduler/TestScheduler.java | 3 + .../controller/ScheduleEngineController.java | 59 +++++++++++++++++++ .../pipeLineDTO/StepExecutionResultDTO.java | 2 +- .../interfacePipe/PipelineStepExecutor.java | 4 +- .../scheduler/service}/PipelineService.java | 12 ++-- .../service/executor}/AIContentExecutor.java | 6 +- .../executor}/BlogPublishExecutor.java | 6 +- .../service/executor}/KeywordExecutor.java | 6 +- .../executor}/ProductCrawlingExecutor.java | 6 +- .../orchestration/TestOrchestration.java | 3 - .../aicontents/scheduler/TestScheduler.java | 3 - .../scheduler/controller/ScheduleEngine.java | 35 ----------- 14 files changed, 87 insertions(+), 63 deletions(-) create mode 100644 springboot/src/main/java/com/softlabs/aicontents/domain/orchestration/TestOrchestration.java rename springboot/src/main/java/com/softlabs/aicontents/{ => domain}/orchestration/enums/PipelineStep.java (89%) create mode 100644 springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/TestScheduler.java create mode 100644 springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/controller/ScheduleEngineController.java rename springboot/src/main/java/com/softlabs/aicontents/{ => domain}/scheduler/dto/pipeLineDTO/StepExecutionResultDTO.java (93%) rename springboot/src/main/java/com/softlabs/aicontents/{ => domain}/scheduler/interfacePipe/PipelineStepExecutor.java (50%) rename springboot/src/main/java/com/softlabs/aicontents/{scheduler/controller => domain/scheduler/service}/PipelineService.java (83%) rename springboot/src/main/java/com/softlabs/aicontents/{scheduler/service => domain/scheduler/service/executor}/AIContentExecutor.java (93%) rename springboot/src/main/java/com/softlabs/aicontents/{scheduler/service => domain/scheduler/service/executor}/BlogPublishExecutor.java (93%) rename springboot/src/main/java/com/softlabs/aicontents/{scheduler/service => domain/scheduler/service/executor}/KeywordExecutor.java (93%) rename springboot/src/main/java/com/softlabs/aicontents/{scheduler/service => domain/scheduler/service/executor}/ProductCrawlingExecutor.java (93%) delete mode 100644 springboot/src/main/java/com/softlabs/aicontents/orchestration/TestOrchestration.java delete mode 100644 springboot/src/main/java/com/softlabs/aicontents/scheduler/TestScheduler.java delete mode 100644 springboot/src/main/java/com/softlabs/aicontents/scheduler/controller/ScheduleEngine.java diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/orchestration/TestOrchestration.java b/springboot/src/main/java/com/softlabs/aicontents/domain/orchestration/TestOrchestration.java new file mode 100644 index 00000000..93e0cd2d --- /dev/null +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/orchestration/TestOrchestration.java @@ -0,0 +1,3 @@ +package com.softlabs.aicontents.domain.orchestration; + +public class TestOrchestration {} diff --git a/springboot/src/main/java/com/softlabs/aicontents/orchestration/enums/PipelineStep.java b/springboot/src/main/java/com/softlabs/aicontents/domain/orchestration/enums/PipelineStep.java similarity index 89% rename from springboot/src/main/java/com/softlabs/aicontents/orchestration/enums/PipelineStep.java rename to springboot/src/main/java/com/softlabs/aicontents/domain/orchestration/enums/PipelineStep.java index 45496d04..6b91eb3b 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/orchestration/enums/PipelineStep.java +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/orchestration/enums/PipelineStep.java @@ -1,4 +1,4 @@ -package com.softlabs.aicontents.orchestration.enums; +package com.softlabs.aicontents.domain.orchestration.enums; public enum PipelineStep { STEP_01("STEP_01", "구글 트렌드 키워드 수집", 1), diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/TestScheduler.java b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/TestScheduler.java new file mode 100644 index 00000000..838cff15 --- /dev/null +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/TestScheduler.java @@ -0,0 +1,3 @@ +package com.softlabs.aicontents.domain.scheduler; + +public class TestScheduler {} diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/controller/ScheduleEngineController.java b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/controller/ScheduleEngineController.java new file mode 100644 index 00000000..1caea52a --- /dev/null +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/controller/ScheduleEngineController.java @@ -0,0 +1,59 @@ +package com.softlabs.aicontents.domain.scheduler.controller; + +import com.softlabs.aicontents.domain.scheduler.service.PipelineService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@Component +@Slf4j +@EnableScheduling +@RestController +@RequestMapping("/v1") +public class ScheduleEngineController { + + private int executionCount = 0; + private final int MAX_executionCount = 3; + private boolean isCompleted = false; + + @Autowired + private PipelineService pipelineService; + + // 초 분 시 일 월 요일 + // @Scheduled(cron = "0 58 21 * * *") + + @Scheduled(cron = "1/5 * * * * *") + /// 12시 18분 정적 실행 + + /// todo : 1. 파이프라인 3회 실행 후 종료(for문) + /// todo : 2. 파이프라인 3회 재시도 /예외처리 + + + ///10. 파이프라인 실행 + @PostMapping("/pipeline/execute") + public void executePipline() { + /// 파이프라인 실행 메서드 호출 + pipelineService.executionPipline(); + } + + + /// 11. 파이프라인 상태 조회 + @GetMapping("/pipeline/status/{executionId}") + public void checkStatus(){ + /// todo : 상태 조회 로직 + } + + /// 12. 파이프라인 제어 + @PostMapping("/pipeline/control/{executionId}") + public void controlPipeline(){ + /// todo : 파이프라인 제어 로직 + } + + } + diff --git a/springboot/src/main/java/com/softlabs/aicontents/scheduler/dto/pipeLineDTO/StepExecutionResultDTO.java b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/pipeLineDTO/StepExecutionResultDTO.java similarity index 93% rename from springboot/src/main/java/com/softlabs/aicontents/scheduler/dto/pipeLineDTO/StepExecutionResultDTO.java rename to springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/pipeLineDTO/StepExecutionResultDTO.java index c0f8ec4a..a0ffde01 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/scheduler/dto/pipeLineDTO/StepExecutionResultDTO.java +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/pipeLineDTO/StepExecutionResultDTO.java @@ -1,4 +1,4 @@ -package com.softlabs.aicontents.scheduler.dto.pipeLineDTO; +package com.softlabs.aicontents.domain.scheduler.dto.pipeLineDTO; import lombok.Getter; diff --git a/springboot/src/main/java/com/softlabs/aicontents/scheduler/interfacePipe/PipelineStepExecutor.java b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/interfacePipe/PipelineStepExecutor.java similarity index 50% rename from springboot/src/main/java/com/softlabs/aicontents/scheduler/interfacePipe/PipelineStepExecutor.java rename to springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/interfacePipe/PipelineStepExecutor.java index 9181be05..7cddbf9b 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/scheduler/interfacePipe/PipelineStepExecutor.java +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/interfacePipe/PipelineStepExecutor.java @@ -1,6 +1,6 @@ -package com.softlabs.aicontents.scheduler.interfacePipe; +package com.softlabs.aicontents.domain.scheduler.interfacePipe; -import com.softlabs.aicontents.scheduler.dto.pipeLineDTO.StepExecutionResultDTO; +import com.softlabs.aicontents.domain.scheduler.dto.pipeLineDTO.StepExecutionResultDTO; public interface PipelineStepExecutor { // 파이프라인 실행관련 공통 인터페이스 diff --git a/springboot/src/main/java/com/softlabs/aicontents/scheduler/controller/PipelineService.java b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/PipelineService.java similarity index 83% rename from springboot/src/main/java/com/softlabs/aicontents/scheduler/controller/PipelineService.java rename to springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/PipelineService.java index 97c0d4f2..0d415de3 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/scheduler/controller/PipelineService.java +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/PipelineService.java @@ -1,10 +1,10 @@ -package com.softlabs.aicontents.scheduler.controller; +package com.softlabs.aicontents.domain.scheduler.service; -import com.softlabs.aicontents.scheduler.dto.pipeLineDTO.StepExecutionResultDTO; -import com.softlabs.aicontents.scheduler.service.AIContentExecutor; -import com.softlabs.aicontents.scheduler.service.BlogPublishExecutor; -import com.softlabs.aicontents.scheduler.service.KeywordExecutor; -import com.softlabs.aicontents.scheduler.service.ProductCrawlingExecutor; +import com.softlabs.aicontents.domain.scheduler.dto.pipeLineDTO.StepExecutionResultDTO; +import com.softlabs.aicontents.domain.scheduler.service.executor.AIContentExecutor; +import com.softlabs.aicontents.domain.scheduler.service.executor.BlogPublishExecutor; +import com.softlabs.aicontents.domain.scheduler.service.executor.KeywordExecutor; +import com.softlabs.aicontents.domain.scheduler.service.executor.ProductCrawlingExecutor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; diff --git a/springboot/src/main/java/com/softlabs/aicontents/scheduler/service/AIContentExecutor.java b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/executor/AIContentExecutor.java similarity index 93% rename from springboot/src/main/java/com/softlabs/aicontents/scheduler/service/AIContentExecutor.java rename to springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/executor/AIContentExecutor.java index 0d34eed0..c6805574 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/scheduler/service/AIContentExecutor.java +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/executor/AIContentExecutor.java @@ -1,8 +1,8 @@ -package com.softlabs.aicontents.scheduler.service; +package com.softlabs.aicontents.domain.scheduler.service.executor; import com.softlabs.aicontents.domain.testService.AIContentService; -import com.softlabs.aicontents.scheduler.dto.pipeLineDTO.StepExecutionResultDTO; -import com.softlabs.aicontents.scheduler.interfacePipe.PipelineStepExecutor; +import com.softlabs.aicontents.domain.scheduler.dto.pipeLineDTO.StepExecutionResultDTO; +import com.softlabs.aicontents.domain.scheduler.interfacePipe.PipelineStepExecutor; // import com.softlabs.aicontents.domain.testMapper.AIContentMapper; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; diff --git a/springboot/src/main/java/com/softlabs/aicontents/scheduler/service/BlogPublishExecutor.java b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/executor/BlogPublishExecutor.java similarity index 93% rename from springboot/src/main/java/com/softlabs/aicontents/scheduler/service/BlogPublishExecutor.java rename to springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/executor/BlogPublishExecutor.java index cf23fb73..4e5caa72 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/scheduler/service/BlogPublishExecutor.java +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/executor/BlogPublishExecutor.java @@ -1,8 +1,8 @@ -package com.softlabs.aicontents.scheduler.service; +package com.softlabs.aicontents.domain.scheduler.service.executor; import com.softlabs.aicontents.domain.testService.BlogPublishService; -import com.softlabs.aicontents.scheduler.dto.pipeLineDTO.StepExecutionResultDTO; -import com.softlabs.aicontents.scheduler.interfacePipe.PipelineStepExecutor; +import com.softlabs.aicontents.domain.scheduler.dto.pipeLineDTO.StepExecutionResultDTO; +import com.softlabs.aicontents.domain.scheduler.interfacePipe.PipelineStepExecutor; // import com.softlabs.aicontents.domain.testMapper.BlogPublishMapper; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; diff --git a/springboot/src/main/java/com/softlabs/aicontents/scheduler/service/KeywordExecutor.java b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/executor/KeywordExecutor.java similarity index 93% rename from springboot/src/main/java/com/softlabs/aicontents/scheduler/service/KeywordExecutor.java rename to springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/executor/KeywordExecutor.java index 0b707570..234b36a7 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/scheduler/service/KeywordExecutor.java +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/executor/KeywordExecutor.java @@ -1,8 +1,8 @@ -package com.softlabs.aicontents.scheduler.service; +package com.softlabs.aicontents.domain.scheduler.service.executor; import com.softlabs.aicontents.domain.testService.KeywordService; -import com.softlabs.aicontents.scheduler.dto.pipeLineDTO.StepExecutionResultDTO; -import com.softlabs.aicontents.scheduler.interfacePipe.PipelineStepExecutor; +import com.softlabs.aicontents.domain.scheduler.dto.pipeLineDTO.StepExecutionResultDTO; +import com.softlabs.aicontents.domain.scheduler.interfacePipe.PipelineStepExecutor; // import com.softlabs.aicontents.domain.testMapper.KeywordMapper; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; diff --git a/springboot/src/main/java/com/softlabs/aicontents/scheduler/service/ProductCrawlingExecutor.java b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/executor/ProductCrawlingExecutor.java similarity index 93% rename from springboot/src/main/java/com/softlabs/aicontents/scheduler/service/ProductCrawlingExecutor.java rename to springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/executor/ProductCrawlingExecutor.java index 33abfd07..12fd8954 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/scheduler/service/ProductCrawlingExecutor.java +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/executor/ProductCrawlingExecutor.java @@ -1,8 +1,8 @@ -package com.softlabs.aicontents.scheduler.service; +package com.softlabs.aicontents.domain.scheduler.service.executor; import com.softlabs.aicontents.domain.testService.ProductCrawlingService; -import com.softlabs.aicontents.scheduler.dto.pipeLineDTO.StepExecutionResultDTO; -import com.softlabs.aicontents.scheduler.interfacePipe.PipelineStepExecutor; +import com.softlabs.aicontents.domain.scheduler.dto.pipeLineDTO.StepExecutionResultDTO; +import com.softlabs.aicontents.domain.scheduler.interfacePipe.PipelineStepExecutor; // import com.softlabs.aicontents.domain.testMapper.ProductCrawlingMapper; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; diff --git a/springboot/src/main/java/com/softlabs/aicontents/orchestration/TestOrchestration.java b/springboot/src/main/java/com/softlabs/aicontents/orchestration/TestOrchestration.java deleted file mode 100644 index acdfe879..00000000 --- a/springboot/src/main/java/com/softlabs/aicontents/orchestration/TestOrchestration.java +++ /dev/null @@ -1,3 +0,0 @@ -package com.softlabs.aicontents.orchestration; - -public class TestOrchestration {} diff --git a/springboot/src/main/java/com/softlabs/aicontents/scheduler/TestScheduler.java b/springboot/src/main/java/com/softlabs/aicontents/scheduler/TestScheduler.java deleted file mode 100644 index ff50365d..00000000 --- a/springboot/src/main/java/com/softlabs/aicontents/scheduler/TestScheduler.java +++ /dev/null @@ -1,3 +0,0 @@ -package com.softlabs.aicontents.scheduler; - -public class TestScheduler {} diff --git a/springboot/src/main/java/com/softlabs/aicontents/scheduler/controller/ScheduleEngine.java b/springboot/src/main/java/com/softlabs/aicontents/scheduler/controller/ScheduleEngine.java deleted file mode 100644 index b0462d27..00000000 --- a/springboot/src/main/java/com/softlabs/aicontents/scheduler/controller/ScheduleEngine.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.softlabs.aicontents.scheduler.controller; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.scheduling.annotation.EnableScheduling; -import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.stereotype.Component; - -@Component -@Slf4j -@EnableScheduling -public class ScheduleEngine { - - private int executionCount = 0; - private final int MAX_executionCount = 3; - private boolean isCompleted = false; - - @Autowired private PipelineService pipelineService; - - // 시간에 따른 자동실행 - // 테스트시에만 사용 - // 초 분 시 일 월 요일 - // 0 7 21 * * * - // @Scheduled(cron = "0 58 21 * * *") - @Scheduled(cron = "1/5 * * * * *") - /// 12시 18분 정적 실행 - - /// todo : 1. 파이프라인 3회 실행 후 종료(for문) - /// todo : 2. 파이프라인 3회 재시도 /예외처리 - - public void executePipline() { - /// 파이프라인 실행 메서드 호출 - pipelineService.executionPipline(); - } -} From 92c41946ddeecf8272dcf591fa34c34a05a02881 Mon Sep 17 00:00:00 2001 From: "skykim5538@gmail.com" Date: Wed, 10 Sep 2025 16:39:45 +0900 Subject: [PATCH 09/20] =?UTF-8?q?chore:=20scheduler,=20orchestration=20?= =?UTF-8?q?=EB=94=94=EB=A0=89=ED=86=A0=EB=A6=AC=20=EC=9C=84=EC=B9=98=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ./gradlew spotlessApply 적용 --- .../common/dto/response/ApiResponseDTO.java | 32 ++++++++----------- .../controller/ScheduleEngineController.java | 19 ++++------- .../service/executor/AIContentExecutor.java | 2 +- .../service/executor/BlogPublishExecutor.java | 2 +- .../service/executor/KeywordExecutor.java | 2 +- .../executor/ProductCrawlingExecutor.java | 2 +- ...4\240\225 API \353\213\264\353\213\271.md" | 1 - 7 files changed, 25 insertions(+), 35 deletions(-) delete mode 100644 "springboot/src/main/java/com/softlabs/aicontents/scheduler/controller/\353\214\200\354\213\234\353\263\264\353\223\234_\354\212\244\354\274\200\354\244\204 \354\204\244\354\240\225 API \353\213\264\353\213\271.md" diff --git a/springboot/src/main/java/com/softlabs/aicontents/common/dto/response/ApiResponseDTO.java b/springboot/src/main/java/com/softlabs/aicontents/common/dto/response/ApiResponseDTO.java index 258c9266..bb11d074 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/common/dto/response/ApiResponseDTO.java +++ b/springboot/src/main/java/com/softlabs/aicontents/common/dto/response/ApiResponseDTO.java @@ -1,23 +1,19 @@ package com.softlabs.aicontents.common.dto.response; // 공통 응답 DTO -public record ApiResponseDTO( - boolean success, - T data, - String message -) { - // 성공 응답 생성 - public static ApiResponseDTO success(T data) { - return new ApiResponseDTO<>(true, data, null); - } +public record ApiResponseDTO(boolean success, T data, String message) { + // 성공 응답 생성 + public static ApiResponseDTO success(T data) { + return new ApiResponseDTO<>(true, data, null); + } - // 성공 응답 + 메시지 - public static ApiResponseDTO success(T data, String message) { - return new ApiResponseDTO<>(true, data, message); - } + // 성공 응답 + 메시지 + public static ApiResponseDTO success(T data, String message) { + return new ApiResponseDTO<>(true, data, message); + } - // 실패 응답 - public static ApiResponseDTO error(String message) { - return new ApiResponseDTO<>(false, null, message); - } -} \ No newline at end of file + // 실패 응답 + public static ApiResponseDTO error(String message) { + return new ApiResponseDTO<>(false, null, message); + } +} diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/controller/ScheduleEngineController.java b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/controller/ScheduleEngineController.java index 1caea52a..4118748a 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/controller/ScheduleEngineController.java +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/controller/ScheduleEngineController.java @@ -22,8 +22,7 @@ public class ScheduleEngineController { private final int MAX_executionCount = 3; private boolean isCompleted = false; - @Autowired - private PipelineService pipelineService; + @Autowired private PipelineService pipelineService; // 초 분 시 일 월 요일 // @Scheduled(cron = "0 58 21 * * *") @@ -34,26 +33,22 @@ public class ScheduleEngineController { /// todo : 1. 파이프라인 3회 실행 후 종료(for문) /// todo : 2. 파이프라인 3회 재시도 /예외처리 - - ///10. 파이프라인 실행 + /// 10. 파이프라인 실행 @PostMapping("/pipeline/execute") public void executePipline() { /// 파이프라인 실행 메서드 호출 pipelineService.executionPipline(); } - - /// 11. 파이프라인 상태 조회 + /// 11. 파이프라인 상태 조회 @GetMapping("/pipeline/status/{executionId}") - public void checkStatus(){ + public void checkStatus() { /// todo : 상태 조회 로직 } /// 12. 파이프라인 제어 @PostMapping("/pipeline/control/{executionId}") - public void controlPipeline(){ - /// todo : 파이프라인 제어 로직 - } - + public void controlPipeline() { + /// todo : 파이프라인 제어 로직 } - +} diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/executor/AIContentExecutor.java b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/executor/AIContentExecutor.java index c6805574..93540f5d 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/executor/AIContentExecutor.java +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/executor/AIContentExecutor.java @@ -1,9 +1,9 @@ package com.softlabs.aicontents.domain.scheduler.service.executor; -import com.softlabs.aicontents.domain.testService.AIContentService; import com.softlabs.aicontents.domain.scheduler.dto.pipeLineDTO.StepExecutionResultDTO; import com.softlabs.aicontents.domain.scheduler.interfacePipe.PipelineStepExecutor; // import com.softlabs.aicontents.domain.testMapper.AIContentMapper; +import com.softlabs.aicontents.domain.testService.AIContentService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/executor/BlogPublishExecutor.java b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/executor/BlogPublishExecutor.java index 4e5caa72..ae3be077 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/executor/BlogPublishExecutor.java +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/executor/BlogPublishExecutor.java @@ -1,9 +1,9 @@ package com.softlabs.aicontents.domain.scheduler.service.executor; -import com.softlabs.aicontents.domain.testService.BlogPublishService; import com.softlabs.aicontents.domain.scheduler.dto.pipeLineDTO.StepExecutionResultDTO; import com.softlabs.aicontents.domain.scheduler.interfacePipe.PipelineStepExecutor; // import com.softlabs.aicontents.domain.testMapper.BlogPublishMapper; +import com.softlabs.aicontents.domain.testService.BlogPublishService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/executor/KeywordExecutor.java b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/executor/KeywordExecutor.java index 234b36a7..e829aade 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/executor/KeywordExecutor.java +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/executor/KeywordExecutor.java @@ -1,9 +1,9 @@ package com.softlabs.aicontents.domain.scheduler.service.executor; -import com.softlabs.aicontents.domain.testService.KeywordService; import com.softlabs.aicontents.domain.scheduler.dto.pipeLineDTO.StepExecutionResultDTO; import com.softlabs.aicontents.domain.scheduler.interfacePipe.PipelineStepExecutor; // import com.softlabs.aicontents.domain.testMapper.KeywordMapper; +import com.softlabs.aicontents.domain.testService.KeywordService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/executor/ProductCrawlingExecutor.java b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/executor/ProductCrawlingExecutor.java index 12fd8954..fb9906f4 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/executor/ProductCrawlingExecutor.java +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/executor/ProductCrawlingExecutor.java @@ -1,9 +1,9 @@ package com.softlabs.aicontents.domain.scheduler.service.executor; -import com.softlabs.aicontents.domain.testService.ProductCrawlingService; import com.softlabs.aicontents.domain.scheduler.dto.pipeLineDTO.StepExecutionResultDTO; import com.softlabs.aicontents.domain.scheduler.interfacePipe.PipelineStepExecutor; // import com.softlabs.aicontents.domain.testMapper.ProductCrawlingMapper; +import com.softlabs.aicontents.domain.testService.ProductCrawlingService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; diff --git "a/springboot/src/main/java/com/softlabs/aicontents/scheduler/controller/\353\214\200\354\213\234\353\263\264\353\223\234_\354\212\244\354\274\200\354\244\204 \354\204\244\354\240\225 API \353\213\264\353\213\271.md" "b/springboot/src/main/java/com/softlabs/aicontents/scheduler/controller/\353\214\200\354\213\234\353\263\264\353\223\234_\354\212\244\354\274\200\354\244\204 \354\204\244\354\240\225 API \353\213\264\353\213\271.md" deleted file mode 100644 index 356e3cbb..00000000 --- "a/springboot/src/main/java/com/softlabs/aicontents/scheduler/controller/\353\214\200\354\213\234\353\263\264\353\223\234_\354\212\244\354\274\200\354\244\204 \354\204\244\354\240\225 API \353\213\264\353\213\271.md" +++ /dev/null @@ -1 +0,0 @@ -대시보드 설정 값-> 스케줄 설정 관련 API 연결 기능 \ No newline at end of file From 9617299f8191cfb3c74b302e57ab7dbb44de91f5 Mon Sep 17 00:00:00 2001 From: "skykim5538@gmail.com" Date: Wed, 10 Sep 2025 20:49:24 +0900 Subject: [PATCH 10/20] =?UTF-8?q?feat:=20ScheduleEngineController=20?= =?UTF-8?q?=EC=97=94=EB=93=9C=ED=8F=AC=EC=9D=B8=ED=8A=B8=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../PipelineService.java | 4 +++- .../orchestration/TestOrchestration.java | 3 --- .../domain/scheduler/TestScheduler.java | 3 --- .../controller/ScheduleEngineController.java | 19 +++++++++++++------ .../service/executor/AIContentExecutor.java | 2 +- .../service/executor/BlogPublishExecutor.java | 2 +- .../service/executor/KeywordExecutor.java | 2 +- .../executor/ProductCrawlingExecutor.java | 2 +- .../domain/scheduler/vo/PipelineStatusVO.java | 10 ++++++++++ .../AIContentService.java | 2 +- .../BlogPublishService.java | 2 +- .../KeywordService.java | 2 +- .../ProductCrawlingService.java | 2 +- 13 files changed, 34 insertions(+), 21 deletions(-) rename springboot/src/main/java/com/softlabs/aicontents/domain/{scheduler/service => orchestration}/PipelineService.java (97%) delete mode 100644 springboot/src/main/java/com/softlabs/aicontents/domain/orchestration/TestOrchestration.java delete mode 100644 springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/TestScheduler.java create mode 100644 springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/vo/PipelineStatusVO.java rename springboot/src/main/java/com/softlabs/aicontents/domain/{testService => testDomainService}/AIContentService.java (76%) rename springboot/src/main/java/com/softlabs/aicontents/domain/{testService => testDomainService}/BlogPublishService.java (76%) rename springboot/src/main/java/com/softlabs/aicontents/domain/{testService => testDomainService}/KeywordService.java (76%) rename springboot/src/main/java/com/softlabs/aicontents/domain/{testService => testDomainService}/ProductCrawlingService.java (77%) diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/PipelineService.java b/springboot/src/main/java/com/softlabs/aicontents/domain/orchestration/PipelineService.java similarity index 97% rename from springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/PipelineService.java rename to springboot/src/main/java/com/softlabs/aicontents/domain/orchestration/PipelineService.java index 0d415de3..4e9651a2 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/PipelineService.java +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/orchestration/PipelineService.java @@ -1,4 +1,4 @@ -package com.softlabs.aicontents.domain.scheduler.service; +package com.softlabs.aicontents.domain.orchestration; import com.softlabs.aicontents.domain.scheduler.dto.pipeLineDTO.StepExecutionResultDTO; import com.softlabs.aicontents.domain.scheduler.service.executor.AIContentExecutor; @@ -53,6 +53,8 @@ public void executionPipline() { log.info("파이프라인 성공"); +// return + } catch (Exception e) { log.error("파이프라인 실행 실패:{}", e.getMessage()); updateExecutionStatus(executionId, "FAILED"); diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/orchestration/TestOrchestration.java b/springboot/src/main/java/com/softlabs/aicontents/domain/orchestration/TestOrchestration.java deleted file mode 100644 index 93e0cd2d..00000000 --- a/springboot/src/main/java/com/softlabs/aicontents/domain/orchestration/TestOrchestration.java +++ /dev/null @@ -1,3 +0,0 @@ -package com.softlabs.aicontents.domain.orchestration; - -public class TestOrchestration {} diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/TestScheduler.java b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/TestScheduler.java deleted file mode 100644 index 838cff15..00000000 --- a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/TestScheduler.java +++ /dev/null @@ -1,3 +0,0 @@ -package com.softlabs.aicontents.domain.scheduler; - -public class TestScheduler {} diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/controller/ScheduleEngineController.java b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/controller/ScheduleEngineController.java index 4118748a..d60e963b 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/controller/ScheduleEngineController.java +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/controller/ScheduleEngineController.java @@ -1,6 +1,6 @@ package com.softlabs.aicontents.domain.scheduler.controller; -import com.softlabs.aicontents.domain.scheduler.service.PipelineService; +import com.softlabs.aicontents.domain.orchestration.PipelineService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.EnableScheduling; @@ -15,14 +15,15 @@ @Slf4j @EnableScheduling @RestController -@RequestMapping("/v1") +@RequestMapping("/v1/pipeline") public class ScheduleEngineController { private int executionCount = 0; private final int MAX_executionCount = 3; private boolean isCompleted = false; - @Autowired private PipelineService pipelineService; + @Autowired + private PipelineService pipelineService; // 초 분 시 일 월 요일 // @Scheduled(cron = "0 58 21 * * *") @@ -34,20 +35,26 @@ public class ScheduleEngineController { /// todo : 2. 파이프라인 3회 재시도 /예외처리 /// 10. 파이프라인 실행 - @PostMapping("/pipeline/execute") + @PostMapping("/execute") public void executePipline() { /// 파이프라인 실행 메서드 호출 pipelineService.executionPipline(); } /// 11. 파이프라인 상태 조회 - @GetMapping("/pipeline/status/{executionId}") + @GetMapping("/status/{executionId}") public void checkStatus() { /// todo : 상태 조회 로직 + /// 파이프 라인이 종료되면, 각 기능들을 지나오면서 + //DB에서 조회한 상태, 키워드, 등등이 VO로 저장되어 있을 것이고 + //이것은 인터페이스에 저장되게 할 것이다. + //그래서 하나의 파이프라인이 끝나면, 이 VO들이 저장된 상태가 되게 한다. + // VO 는 대시보드에서 요청하는 DTO + //결과적으로 VO를 DTO로 보내줘야 함. } /// 12. 파이프라인 제어 - @PostMapping("/pipeline/control/{executionId}") + @PostMapping("/control/{executionId}") public void controlPipeline() { /// todo : 파이프라인 제어 로직 } diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/executor/AIContentExecutor.java b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/executor/AIContentExecutor.java index 93540f5d..5b4aaa06 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/executor/AIContentExecutor.java +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/executor/AIContentExecutor.java @@ -3,7 +3,7 @@ import com.softlabs.aicontents.domain.scheduler.dto.pipeLineDTO.StepExecutionResultDTO; import com.softlabs.aicontents.domain.scheduler.interfacePipe.PipelineStepExecutor; // import com.softlabs.aicontents.domain.testMapper.AIContentMapper; -import com.softlabs.aicontents.domain.testService.AIContentService; +import com.softlabs.aicontents.domain.testDomainService.AIContentService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/executor/BlogPublishExecutor.java b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/executor/BlogPublishExecutor.java index ae3be077..e369af52 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/executor/BlogPublishExecutor.java +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/executor/BlogPublishExecutor.java @@ -3,7 +3,7 @@ import com.softlabs.aicontents.domain.scheduler.dto.pipeLineDTO.StepExecutionResultDTO; import com.softlabs.aicontents.domain.scheduler.interfacePipe.PipelineStepExecutor; // import com.softlabs.aicontents.domain.testMapper.BlogPublishMapper; -import com.softlabs.aicontents.domain.testService.BlogPublishService; +import com.softlabs.aicontents.domain.testDomainService.BlogPublishService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/executor/KeywordExecutor.java b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/executor/KeywordExecutor.java index e829aade..dcb8e233 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/executor/KeywordExecutor.java +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/executor/KeywordExecutor.java @@ -3,7 +3,7 @@ import com.softlabs.aicontents.domain.scheduler.dto.pipeLineDTO.StepExecutionResultDTO; import com.softlabs.aicontents.domain.scheduler.interfacePipe.PipelineStepExecutor; // import com.softlabs.aicontents.domain.testMapper.KeywordMapper; -import com.softlabs.aicontents.domain.testService.KeywordService; +import com.softlabs.aicontents.domain.testDomainService.KeywordService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/executor/ProductCrawlingExecutor.java b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/executor/ProductCrawlingExecutor.java index fb9906f4..22e76ef9 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/executor/ProductCrawlingExecutor.java +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/executor/ProductCrawlingExecutor.java @@ -3,7 +3,7 @@ import com.softlabs.aicontents.domain.scheduler.dto.pipeLineDTO.StepExecutionResultDTO; import com.softlabs.aicontents.domain.scheduler.interfacePipe.PipelineStepExecutor; // import com.softlabs.aicontents.domain.testMapper.ProductCrawlingMapper; -import com.softlabs.aicontents.domain.testService.ProductCrawlingService; +import com.softlabs.aicontents.domain.testDomainService.ProductCrawlingService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/vo/PipelineStatusVO.java b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/vo/PipelineStatusVO.java new file mode 100644 index 00000000..1b2ba173 --- /dev/null +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/vo/PipelineStatusVO.java @@ -0,0 +1,10 @@ +package com.softlabs.aicontents.domain.scheduler.vo; + +public record PipelineStatusVO( + + /// === 성공 시 === + + + +) { +} diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/testService/AIContentService.java b/springboot/src/main/java/com/softlabs/aicontents/domain/testDomainService/AIContentService.java similarity index 76% rename from springboot/src/main/java/com/softlabs/aicontents/domain/testService/AIContentService.java rename to springboot/src/main/java/com/softlabs/aicontents/domain/testDomainService/AIContentService.java index e7e1cfc4..87fc4e0c 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/domain/testService/AIContentService.java +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/testDomainService/AIContentService.java @@ -1,4 +1,4 @@ -package com.softlabs.aicontents.domain.testService; +package com.softlabs.aicontents.domain.testDomainService; import org.springframework.stereotype.Service; diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/testService/BlogPublishService.java b/springboot/src/main/java/com/softlabs/aicontents/domain/testDomainService/BlogPublishService.java similarity index 76% rename from springboot/src/main/java/com/softlabs/aicontents/domain/testService/BlogPublishService.java rename to springboot/src/main/java/com/softlabs/aicontents/domain/testDomainService/BlogPublishService.java index fa301945..0968c6ba 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/domain/testService/BlogPublishService.java +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/testDomainService/BlogPublishService.java @@ -1,4 +1,4 @@ -package com.softlabs.aicontents.domain.testService; +package com.softlabs.aicontents.domain.testDomainService; import org.springframework.stereotype.Service; diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/testService/KeywordService.java b/springboot/src/main/java/com/softlabs/aicontents/domain/testDomainService/KeywordService.java similarity index 76% rename from springboot/src/main/java/com/softlabs/aicontents/domain/testService/KeywordService.java rename to springboot/src/main/java/com/softlabs/aicontents/domain/testDomainService/KeywordService.java index eaf43808..e16fd60f 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/domain/testService/KeywordService.java +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/testDomainService/KeywordService.java @@ -1,4 +1,4 @@ -package com.softlabs.aicontents.domain.testService; +package com.softlabs.aicontents.domain.testDomainService; import org.springframework.stereotype.Service; diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/testService/ProductCrawlingService.java b/springboot/src/main/java/com/softlabs/aicontents/domain/testDomainService/ProductCrawlingService.java similarity index 77% rename from springboot/src/main/java/com/softlabs/aicontents/domain/testService/ProductCrawlingService.java rename to springboot/src/main/java/com/softlabs/aicontents/domain/testDomainService/ProductCrawlingService.java index d9be2ac2..aff37107 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/domain/testService/ProductCrawlingService.java +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/testDomainService/ProductCrawlingService.java @@ -1,4 +1,4 @@ -package com.softlabs.aicontents.domain.testService; +package com.softlabs.aicontents.domain.testDomainService; import org.springframework.stereotype.Service; From ecd8928991fccc2599442b98303284d087c1a222 Mon Sep 17 00:00:00 2001 From: "skykim5538@gmail.com" Date: Thu, 11 Sep 2025 16:28:23 +0900 Subject: [PATCH 11/20] =?UTF-8?q?feat:=20=EC=9D=91=EB=8B=B5=20=EA=B0=9D?= =?UTF-8?q?=EC=B2=B4=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/scheduler/dto/PipeResultDTO.java | 42 +++++++++++++++++++ .../scheduler/dto/resultDTO/Content.java | 15 +++++++ .../dto/resultDTO/ContentGeneration.java | 10 +++++ .../dto/resultDTO/ContentPublishing.java | 10 +++++ .../dto/resultDTO/ExecutionResults.java | 18 ++++++++ .../scheduler/dto/resultDTO/Keyword.java | 13 ++++++ .../dto/resultDTO/KeywordExtraction.java | 11 +++++ .../domain/scheduler/dto/resultDTO/Logs.java | 16 +++++++ .../scheduler/dto/resultDTO/Product.java | 14 +++++++ .../dto/resultDTO/ProductCrawling.java | 10 +++++ .../dto/resultDTO/ProgressResult.java | 13 ++++++ .../dto/resultDTO/PublishingStatus.java | 13 ++++++ 12 files changed, 185 insertions(+) create mode 100644 springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/PipeResultDTO.java create mode 100644 springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/resultDTO/Content.java create mode 100644 springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/resultDTO/ContentGeneration.java create mode 100644 springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/resultDTO/ContentPublishing.java create mode 100644 springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/resultDTO/ExecutionResults.java create mode 100644 springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/resultDTO/Keyword.java create mode 100644 springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/resultDTO/KeywordExtraction.java create mode 100644 springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/resultDTO/Logs.java create mode 100644 springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/resultDTO/Product.java create mode 100644 springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/resultDTO/ProductCrawling.java create mode 100644 springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/resultDTO/ProgressResult.java create mode 100644 springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/resultDTO/PublishingStatus.java diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/PipeResultDTO.java b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/PipeResultDTO.java new file mode 100644 index 00000000..d5b45e36 --- /dev/null +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/PipeResultDTO.java @@ -0,0 +1,42 @@ +package com.softlabs.aicontents.domain.scheduler.dto; + +import com.softlabs.aicontents.domain.scheduler.dto.resultDTO.ExecutionResults; +import com.softlabs.aicontents.domain.scheduler.dto.resultDTO.Logs; +import com.softlabs.aicontents.domain.scheduler.dto.resultDTO.ProgressResult; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +//최상위 응답 +@Data +@Schema +public class PipeResultDTO { + + boolean success; + PipelineDataDTO data; + + + @Data + public class PipelineDataDTO { + + // 실행정보 + Long executionId; + String overallStatus; + String startedAt; + String completedAt; + String currentStage; + + + // 각 단계별 진행 상황 + ProgressResult progressResult; + + //단계별 결과 데이터 + ExecutionResults results; + + // 로그 정보 + List logs; + + + } +} diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/resultDTO/Content.java b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/resultDTO/Content.java new file mode 100644 index 00000000..5bf02ac4 --- /dev/null +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/resultDTO/Content.java @@ -0,0 +1,15 @@ +package com.softlabs.aicontents.domain.scheduler.dto.resultDTO; + +import java.util.List; +import lombok.Data; + + + +@Data +public class Content { + String title; + String content; + List tags; + + +} diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/resultDTO/ContentGeneration.java b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/resultDTO/ContentGeneration.java new file mode 100644 index 00000000..5b1a28e9 --- /dev/null +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/resultDTO/ContentGeneration.java @@ -0,0 +1,10 @@ +package com.softlabs.aicontents.domain.scheduler.dto.resultDTO; +import lombok.Data; + + + +@Data +public class ContentGeneration { + String status; + int progress; +} diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/resultDTO/ContentPublishing.java b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/resultDTO/ContentPublishing.java new file mode 100644 index 00000000..0fb2974a --- /dev/null +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/resultDTO/ContentPublishing.java @@ -0,0 +1,10 @@ +package com.softlabs.aicontents.domain.scheduler.dto.resultDTO; +import lombok.Data; + + + +@Data +public class ContentPublishing { + String status; + int progress; +} diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/resultDTO/ExecutionResults.java b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/resultDTO/ExecutionResults.java new file mode 100644 index 00000000..4262009f --- /dev/null +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/resultDTO/ExecutionResults.java @@ -0,0 +1,18 @@ +package com.softlabs.aicontents.domain.scheduler.dto.resultDTO; + + +import java.util.List; + +import lombok.Data; + + + +@Data +public class ExecutionResults { + List keywords; + List products; + Content content; + PublishingStatus publishingStatus; + + +} diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/resultDTO/Keyword.java b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/resultDTO/Keyword.java new file mode 100644 index 00000000..e1018b49 --- /dev/null +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/resultDTO/Keyword.java @@ -0,0 +1,13 @@ +package com.softlabs.aicontents.domain.scheduler.dto.resultDTO; +import lombok.Data; + + + +@Data +public class Keyword { + + String keyword; + boolean selected; + int relevanceScore; + +} diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/resultDTO/KeywordExtraction.java b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/resultDTO/KeywordExtraction.java new file mode 100644 index 00000000..aeb49bbd --- /dev/null +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/resultDTO/KeywordExtraction.java @@ -0,0 +1,11 @@ +package com.softlabs.aicontents.domain.scheduler.dto.resultDTO; +import lombok.Data; + + + +@Data +public class KeywordExtraction { + + String status; + int progress; +} diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/resultDTO/Logs.java b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/resultDTO/Logs.java new file mode 100644 index 00000000..9bc0b95d --- /dev/null +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/resultDTO/Logs.java @@ -0,0 +1,16 @@ +package com.softlabs.aicontents.domain.scheduler.dto.resultDTO; + +import lombok.Data; + + + +@Data +public class Logs { + + String timestamp; + String stage; + String level; + String message; + + +} diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/resultDTO/Product.java b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/resultDTO/Product.java new file mode 100644 index 00000000..3aedd450 --- /dev/null +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/resultDTO/Product.java @@ -0,0 +1,14 @@ +package com.softlabs.aicontents.domain.scheduler.dto.resultDTO; +import lombok.Data; + + + +@Data +public class Product { + + String productId; + String name; + int price; + String platform; + +} diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/resultDTO/ProductCrawling.java b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/resultDTO/ProductCrawling.java new file mode 100644 index 00000000..921bb02c --- /dev/null +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/resultDTO/ProductCrawling.java @@ -0,0 +1,10 @@ +package com.softlabs.aicontents.domain.scheduler.dto.resultDTO; +import lombok.Data; + + + +@Data +public class ProductCrawling { + String status; + int progress; +} diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/resultDTO/ProgressResult.java b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/resultDTO/ProgressResult.java new file mode 100644 index 00000000..8cd7d988 --- /dev/null +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/resultDTO/ProgressResult.java @@ -0,0 +1,13 @@ +package com.softlabs.aicontents.domain.scheduler.dto.resultDTO; +import lombok.Data; + + + +@Data +public class ProgressResult { + + KeywordExtraction keywordExtraction; + ProductCrawling productCrawling; + ContentGeneration contentGeneration; + ContentPublishing contentPublishing; +} diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/resultDTO/PublishingStatus.java b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/resultDTO/PublishingStatus.java new file mode 100644 index 00000000..8d19a305 --- /dev/null +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/resultDTO/PublishingStatus.java @@ -0,0 +1,13 @@ +package com.softlabs.aicontents.domain.scheduler.dto.resultDTO; +import lombok.Data; + + + +@Data +public class PublishingStatus { + + String platform; + String status; + String url; + +} From 049a0bfb089de017cf0a1dfd949fa5a5f9bf1936 Mon Sep 17 00:00:00 2001 From: "skykim5538@gmail.com" Date: Thu, 11 Sep 2025 17:01:34 +0900 Subject: [PATCH 12/20] =?UTF-8?q?feat:=20=EC=83=81=ED=83=9C=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/orchestration/PipelineService.java | 14 +++++----- .../controller/ScheduleEngineController.java | 27 ++++++++++++++----- ...eResultDTO.java => PipeResultDataDTO.java} | 12 +++------ .../interfacePipe/PipelineStepExecutor.java | 4 ++- .../service/executor/AIContentExecutor.java | 2 +- .../service/executor/BlogPublishExecutor.java | 22 +++------------ .../service/executor/KeywordExecutor.java | 2 +- .../executor/ProductCrawlingExecutor.java | 2 +- 8 files changed, 40 insertions(+), 45 deletions(-) rename springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/{PipeResultDTO.java => PipeResultDataDTO.java} (83%) diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/orchestration/PipelineService.java b/springboot/src/main/java/com/softlabs/aicontents/domain/orchestration/PipelineService.java index 4e9651a2..b2abeb33 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/domain/orchestration/PipelineService.java +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/orchestration/PipelineService.java @@ -1,5 +1,6 @@ package com.softlabs.aicontents.domain.orchestration; +import com.softlabs.aicontents.domain.scheduler.dto.PipeResultDataDTO; import com.softlabs.aicontents.domain.scheduler.dto.pipeLineDTO.StepExecutionResultDTO; import com.softlabs.aicontents.domain.scheduler.service.executor.AIContentExecutor; import com.softlabs.aicontents.domain.scheduler.service.executor.BlogPublishExecutor; @@ -24,8 +25,8 @@ public class PipelineService { @Autowired private BlogPublishExecutor blogExecutor; - public void executionPipline() { - Long executionId = createNewExecution(); + public PipeResultDataDTO executionPipline() { + int executionId = createNewExecution(); // todo : executionId = (동일한 파이프라인인지 구분하는 용도) // DB 에서 PIPELINE_EXECUTIONS 테이블의 execution_id @@ -53,24 +54,25 @@ public void executionPipline() { log.info("파이프라인 성공"); -// return + return new PipeResultDataDTO(); } catch (Exception e) { log.error("파이프라인 실행 실패:{}", e.getMessage()); updateExecutionStatus(executionId, "FAILED"); } + return null; } - private Long createNewExecution() { + private int createNewExecution() { - return 0L; /// 일단은 Long타입의 기본값. + return 0; /// 일단은 Long타입의 기본값. // todo: return 반환값으로, // PIPELINE_EXECUTIONS 테이블에서 executionId를 새로 생성하고, // 이것을 가져오는 메서드 구현 // => 파이프라인이 새로 실행될 때마다 executionId를 생성 } - private void updateExecutionStatus(Long executionId, String failed) { + private void updateExecutionStatus(int executionId, String failed) { // todo: PIPELINE_EXECUTIONS에 상태 업데이트하는 코드 구현(SUCCESS, FAILED,PENDING 등등등) } } diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/controller/ScheduleEngineController.java b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/controller/ScheduleEngineController.java index d60e963b..eb083958 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/controller/ScheduleEngineController.java +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/controller/ScheduleEngineController.java @@ -1,6 +1,8 @@ package com.softlabs.aicontents.domain.scheduler.controller; +import com.softlabs.aicontents.common.dto.response.ApiResponseDTO; import com.softlabs.aicontents.domain.orchestration.PipelineService; +import com.softlabs.aicontents.domain.scheduler.dto.PipeResultDataDTO; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.EnableScheduling; @@ -28,22 +30,33 @@ public class ScheduleEngineController { // 초 분 시 일 월 요일 // @Scheduled(cron = "0 58 21 * * *") - @Scheduled(cron = "1/5 * * * * *") + @Scheduled(cron = "1/30 * * * * *") /// 12시 18분 정적 실행 /// todo : 1. 파이프라인 3회 실행 후 종료(for문) /// todo : 2. 파이프라인 3회 재시도 /예외처리 /// 10. 파이프라인 실행 - @PostMapping("/execute") - public void executePipline() { - /// 파이프라인 실행 메서드 호출 - pipelineService.executionPipline(); - } +// @PostMapping("/execute") +// public void executePipline() { +// /// 파이프라인 실행 메서드 호출 +// pipelineService.executionPipline(); +// } /// 11. 파이프라인 상태 조회 @GetMapping("/status/{executionId}") - public void checkStatus() { + public ApiResponseDTO checkStatus() { + + try { + + PipeResultDataDTO pipeResultDataDTO = pipelineService.executionPipline(); + String successMesg = "파이프라인 상태 데이터를 pipeResultDataDTO에 저장 완료"; + + return ApiResponseDTO.success(pipeResultDataDTO, successMesg); + + }catch (Exception e){ + return ApiResponseDTO.error("파이프라인 상태 조회 실패"); + } /// todo : 상태 조회 로직 /// 파이프 라인이 종료되면, 각 기능들을 지나오면서 //DB에서 조회한 상태, 키워드, 등등이 VO로 저장되어 있을 것이고 diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/PipeResultDTO.java b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/PipeResultDataDTO.java similarity index 83% rename from springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/PipeResultDTO.java rename to springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/PipeResultDataDTO.java index d5b45e36..ec338f18 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/PipeResultDTO.java +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/PipeResultDataDTO.java @@ -11,17 +11,11 @@ //최상위 응답 @Data @Schema -public class PipeResultDTO { - boolean success; - PipelineDataDTO data; - - - @Data - public class PipelineDataDTO { +public class PipeResultDataDTO { // 실행정보 - Long executionId; + int executionId; String overallStatus; String startedAt; String completedAt; @@ -39,4 +33,4 @@ public class PipelineDataDTO { } -} + diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/interfacePipe/PipelineStepExecutor.java b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/interfacePipe/PipelineStepExecutor.java index 7cddbf9b..670891e2 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/interfacePipe/PipelineStepExecutor.java +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/interfacePipe/PipelineStepExecutor.java @@ -5,5 +5,7 @@ public interface PipelineStepExecutor { // 파이프라인 실행관련 공통 인터페이스 - StepExecutionResultDTO execute(Long executionId); + StepExecutionResultDTO execute(int executionId); + + } diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/executor/AIContentExecutor.java b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/executor/AIContentExecutor.java index 5b4aaa06..d6a05512 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/executor/AIContentExecutor.java +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/executor/AIContentExecutor.java @@ -23,7 +23,7 @@ public class AIContentExecutor implements PipelineStepExecutor { // // todo: 실제 LLM생성 매퍼 인터페이스로 변경 @Override - public StepExecutionResultDTO execute(Long executionId) { + public StepExecutionResultDTO execute(int executionId) { /// test : 파이프라인 동작 테스트 System.out.println("LLM생성 메서드 호출/ 실행"); diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/executor/BlogPublishExecutor.java b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/executor/BlogPublishExecutor.java index e369af52..76a506bf 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/executor/BlogPublishExecutor.java +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/executor/BlogPublishExecutor.java @@ -13,7 +13,8 @@ @Slf4j @Service public class BlogPublishExecutor implements PipelineStepExecutor { - @Autowired private BlogPublishService blogPublishService; + @Autowired + private BlogPublishService blogPublishService; // todo: 실제 발행 클래스로 변경 @@ -22,38 +23,21 @@ public class BlogPublishExecutor implements PipelineStepExecutor { // // todo: 실제 발행 매퍼 인터페이스로 변경 @Override - public StepExecutionResultDTO execute(Long executionId) { + public StepExecutionResultDTO execute(int executionId) { /// test : 파이프라인 동작 테스트 System.out.println("발행 메서드 호출/ 실행"); - delayWithDots(3); /// todo : 테스트용 RDS 조회 쿼리 System.out.println("발행 결과 DB에서 쿼리 조회"); - delayWithDots(3); System.out.println("발행 결과 DB 완료 확인 로직 실행"); - delayWithDots(3); System.out.println("발행 상태 판단 -> 완료(success)"); System.out.println("발행 상태 판단 -> 실패(failure)-> 재시도/예외처리"); - delayWithDots(3); System.out.println("[발행] 완료"); - delayWithDots(3); return null; /// todo : 반환 값으로 이전 기능이 요구하는 파라메터를 반환하기. } - /// 테스트용 딜레이 메서드 - private void delayWithDots(int seconds) { - try { - for (int i = 0; i < seconds; i++) { - Thread.sleep(200); // 1초마다 - System.out.print("."); - } - System.out.println(); // 줄바꿈 - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - } } // try { diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/executor/KeywordExecutor.java b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/executor/KeywordExecutor.java index dcb8e233..fc3f9db6 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/executor/KeywordExecutor.java +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/executor/KeywordExecutor.java @@ -22,7 +22,7 @@ public class KeywordExecutor implements PipelineStepExecutor { // private KeywordMapper keywordMapper; // DB 조회용 @Override - public StepExecutionResultDTO execute(Long executionId) { + public StepExecutionResultDTO execute(int executionId) { /// test : 파이프라인 동작 테스트 System.out.println("키워드 수집 메서드 호출/ 실행"); diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/executor/ProductCrawlingExecutor.java b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/executor/ProductCrawlingExecutor.java index 22e76ef9..99f69b99 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/executor/ProductCrawlingExecutor.java +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/executor/ProductCrawlingExecutor.java @@ -23,7 +23,7 @@ public class ProductCrawlingExecutor implements PipelineStepExecutor { // // todo: 실제 싸다구 정보 수집 매퍼 인터페이스로 변경 @Override - public StepExecutionResultDTO execute(Long executionId) { + public StepExecutionResultDTO execute(int executionId) { /// test : 파이프라인 동작 테스트 System.out.println("싸다구 정보 수집 메서드 호출/ 실행"); From 4a23ae473d2c171b993afa7185f5e9f1ca67b38e Mon Sep 17 00:00:00 2001 From: JungSoonIn Date: Thu, 11 Sep 2025 22:04:05 +0900 Subject: [PATCH 13/20] =?UTF-8?q?feat:=20=ED=91=9C=EC=A4=80=ED=99=94?= =?UTF-8?q?=EB=90=9C=20=EC=97=90=EB=9F=AC=20=EC=B2=98=EB=A6=AC=EB=A5=BC=20?= =?UTF-8?q?=EC=9C=84=ED=95=9C=20ErrorCode=20enum=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../aicontents/common/enums/ErrorCode.java | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 springboot/src/main/java/com/softlabs/aicontents/common/enums/ErrorCode.java diff --git a/springboot/src/main/java/com/softlabs/aicontents/common/enums/ErrorCode.java b/springboot/src/main/java/com/softlabs/aicontents/common/enums/ErrorCode.java new file mode 100644 index 00000000..8eb5de8b --- /dev/null +++ b/springboot/src/main/java/com/softlabs/aicontents/common/enums/ErrorCode.java @@ -0,0 +1,60 @@ +package com.softlabs.aicontents.common.enums; + +import org.springframework.http.HttpStatus; + +public enum ErrorCode { + + // 400번대 클라이언트 에러 + BAD_REQUEST(HttpStatus.BAD_REQUEST, "E001", "잘못된 요청입니다."), + INVALID_INPUT(HttpStatus.BAD_REQUEST, "E002", "입력값이 올바르지 않습니다."), + MISSING_REQUIRED_FIELD(HttpStatus.BAD_REQUEST, "E003", "필수 필드가 누락되었습니다."), + INVALID_FORMAT(HttpStatus.BAD_REQUEST, "E004", "올바르지 않은 형식입니다."), + + UNAUTHORIZED(HttpStatus.UNAUTHORIZED, "E101", "인증이 필요합니다."), + INVALID_TOKEN(HttpStatus.UNAUTHORIZED, "E102", "유효하지 않은 토큰입니다."), + TOKEN_EXPIRED(HttpStatus.UNAUTHORIZED, "E103", "토큰이 만료되었습니다."), + + FORBIDDEN(HttpStatus.FORBIDDEN, "E201", "접근 권한이 없습니다."), + INSUFFICIENT_PERMISSIONS(HttpStatus.FORBIDDEN, "E202", "권한이 부족합니다."), + + NOT_FOUND(HttpStatus.NOT_FOUND, "E301", "요청한 리소스를 찾을 수 없습니다."), + USER_NOT_FOUND(HttpStatus.NOT_FOUND, "E302", "사용자를 찾을 수 없습니다."), + DATA_NOT_FOUND(HttpStatus.NOT_FOUND, "E303", "데이터를 찾을 수 없습니다."), + + METHOD_NOT_ALLOWED(HttpStatus.METHOD_NOT_ALLOWED, "E401", "허용되지 않은 HTTP 메서드입니다."), + + CONFLICT(HttpStatus.CONFLICT, "E501", "데이터 충돌이 발생했습니다."), + DUPLICATE_RESOURCE(HttpStatus.CONFLICT, "E502", "이미 존재하는 리소스입니다."), + + // 500번대 서버 에러 + INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "E901", "내부 서버 오류가 발생했습니다."), + DATABASE_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "E902", "데이터베이스 오류가 발생했습니다."), + EXTERNAL_API_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "E903", "외부 API 호출 중 오류가 발생했습니다."), + SERVICE_UNAVAILABLE(HttpStatus.SERVICE_UNAVAILABLE, "E904", "서비스를 사용할 수 없습니다."); + + private final HttpStatus httpStatus; + private final String code; + private final String message; + + ErrorCode(HttpStatus httpStatus, String code, String message) { + this.httpStatus = httpStatus; + this.code = code; + this.message = message; + } + + public HttpStatus getHttpStatus() { + return httpStatus; + } + + public String getCode() { + return code; + } + + public String getMessage() { + return message; + } + + public int getStatus() { + return httpStatus.value(); + } +} \ No newline at end of file From 20a5cab24b2e6739158cdf8ff576edb7912766ed Mon Sep 17 00:00:00 2001 From: JungSoonIn Date: Thu, 11 Sep 2025 22:04:21 +0900 Subject: [PATCH 14/20] =?UTF-8?q?feat:=20=EB=B9=84=EC=A6=88=EB=8B=88?= =?UTF-8?q?=EC=8A=A4=20=EB=A1=9C=EC=A7=81=20=EC=98=88=EC=99=B8=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=EB=A5=BC=20=EC=9C=84=ED=95=9C=20BusinessException=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/exception/BusinessException.java | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 springboot/src/main/java/com/softlabs/aicontents/common/exception/BusinessException.java diff --git a/springboot/src/main/java/com/softlabs/aicontents/common/exception/BusinessException.java b/springboot/src/main/java/com/softlabs/aicontents/common/exception/BusinessException.java new file mode 100644 index 00000000..ace67e19 --- /dev/null +++ b/springboot/src/main/java/com/softlabs/aicontents/common/exception/BusinessException.java @@ -0,0 +1,68 @@ +package com.softlabs.aicontents.common.exception; + +import com.softlabs.aicontents.common.enums.ErrorCode; + +public class BusinessException extends RuntimeException { + + private final ErrorCode errorCode; + private final String customMessage; + + //기본 생성자 (ErrorCode만 사용) + //사용 예시 throw new BusinessException(ErrorCode.USER_NOT_FOUND); + public BusinessException(ErrorCode errorCode) { + super(errorCode.getMessage()); + this.errorCode = errorCode; + this.customMessage = null; + } + + //커스텀 메시지 사용 가능 + //사용 예시 throw new BusinessException(ErrorCode.INVALID_INPUT, "이메일 형식이 잘못되었습니다."); + public BusinessException(ErrorCode errorCode, String customMessage) { + super(customMessage); + this.errorCode = errorCode; + this.customMessage = customMessage; + } + + //원인 예외(cause) 전달 가능 + //사용 예시 + // try { + // externalApi.call(); + //} catch (IOException e) { + // throw new BusinessException(ErrorCode.EXTERNAL_API_ERROR, e); + //} + public BusinessException(ErrorCode errorCode, Throwable cause) { + super(errorCode.getMessage(), cause); + this.errorCode = errorCode; + this.customMessage = null; + } + + + //커스텀 메시지 + 원인 예외 둘 다 사용 + //사용 예시 + //try { + // paymentGateway.call(); + //} catch (Exception e) { + // throw new BusinessException( + // ErrorCode.EXTERNAL_API_ERROR, + // "결제 서비스 호출 중 오류 발생", + // e + // ); + //} + public BusinessException(ErrorCode errorCode, String customMessage, Throwable cause) { + super(customMessage, cause); + this.errorCode = errorCode; + this.customMessage = customMessage; + } + + public ErrorCode getErrorCode() { + return errorCode; + } + + public String getCustomMessage() { + return customMessage; + } + + public String getEffectiveMessage() { + return customMessage != null ? customMessage : errorCode.getMessage(); + } +} \ No newline at end of file From b30401e8ea5ef6bae2df2b610f10b16253d2fa0f Mon Sep 17 00:00:00 2001 From: JungSoonIn Date: Thu, 11 Sep 2025 22:04:37 +0900 Subject: [PATCH 15/20] =?UTF-8?q?feat:=20=ED=91=9C=EC=A4=80=ED=99=94?= =?UTF-8?q?=EB=90=9C=20=EC=97=90=EB=9F=AC=20=EC=9D=91=EB=8B=B5=EC=9D=84=20?= =?UTF-8?q?=EC=9C=84=ED=95=9C=20ErrorResponseDTO=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/dto/response/ErrorResponseDTO.java | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 springboot/src/main/java/com/softlabs/aicontents/common/dto/response/ErrorResponseDTO.java diff --git a/springboot/src/main/java/com/softlabs/aicontents/common/dto/response/ErrorResponseDTO.java b/springboot/src/main/java/com/softlabs/aicontents/common/dto/response/ErrorResponseDTO.java new file mode 100644 index 00000000..c37609ac --- /dev/null +++ b/springboot/src/main/java/com/softlabs/aicontents/common/dto/response/ErrorResponseDTO.java @@ -0,0 +1,49 @@ +package com.softlabs.aicontents.common.dto.response; + +import com.softlabs.aicontents.common.enums.ErrorCode; +import com.softlabs.aicontents.common.util.TraceIdUtil; + +import java.time.LocalDateTime; + +public record ErrorResponseDTO( + LocalDateTime timestamp, + String code, + String message, + String traceId, + String path, + int status +) { + + public static ErrorResponseDTO of(ErrorCode errorCode, String path) { + return new ErrorResponseDTO( + LocalDateTime.now(), + errorCode.getCode(), + errorCode.getMessage(), + TraceIdUtil.getOrCreateTraceId(), + path, + errorCode.getStatus() + ); + } + + public static ErrorResponseDTO of(ErrorCode errorCode, String path, String customMessage) { + return new ErrorResponseDTO( + LocalDateTime.now(), + errorCode.getCode(), + customMessage, + TraceIdUtil.getOrCreateTraceId(), + path, + errorCode.getStatus() + ); + } + + public static ErrorResponseDTO ofWithTraceId(ErrorCode errorCode, String path, String traceId) { + return new ErrorResponseDTO( + LocalDateTime.now(), + errorCode.getCode(), + errorCode.getMessage(), + traceId, + path, + errorCode.getStatus() + ); + } +} \ No newline at end of file From f5d46f760171e774a870b169c53e3b3de5360fa7 Mon Sep 17 00:00:00 2001 From: JungSoonIn Date: Thu, 11 Sep 2025 22:04:52 +0900 Subject: [PATCH 16/20] =?UTF-8?q?feat:=20=EC=A0=84=EC=97=AD=20=EC=98=88?= =?UTF-8?q?=EC=99=B8=20=EC=B2=98=EB=A6=AC=EB=A5=BC=20=EC=9C=84=ED=95=9C=20?= =?UTF-8?q?GlobalExceptionHandler=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exception/GlobalExceptionHandler.java | 207 ++++++++++++++++++ 1 file changed, 207 insertions(+) create mode 100644 springboot/src/main/java/com/softlabs/aicontents/common/exception/GlobalExceptionHandler.java diff --git a/springboot/src/main/java/com/softlabs/aicontents/common/exception/GlobalExceptionHandler.java b/springboot/src/main/java/com/softlabs/aicontents/common/exception/GlobalExceptionHandler.java new file mode 100644 index 00000000..9c1a7283 --- /dev/null +++ b/springboot/src/main/java/com/softlabs/aicontents/common/exception/GlobalExceptionHandler.java @@ -0,0 +1,207 @@ +package com.softlabs.aicontents.common.exception; + +import com.softlabs.aicontents.common.dto.response.ErrorResponseDTO; +import com.softlabs.aicontents.common.enums.ErrorCode; +import jakarta.servlet.http.HttpServletRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.ResponseEntity; +import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.validation.BindException; +import org.springframework.validation.FieldError; +import org.springframework.web.HttpRequestMethodNotSupportedException; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.MissingServletRequestParameterException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; +import org.springframework.web.servlet.NoHandlerFoundException; + +import java.util.stream.Collectors; + +@RestControllerAdvice +public class GlobalExceptionHandler { + + private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class); + + //BusinessException에 정의된 예외 처리 + @ExceptionHandler(BusinessException.class) + public ResponseEntity handleBusinessException( + BusinessException ex, + HttpServletRequest request) { + + log.warn("Business exception occurred: {}", ex.getMessage(), ex); + + ErrorCode errorCode = ex.getErrorCode(); + ErrorResponseDTO errorResponse = ErrorResponseDTO.of( + errorCode, + request.getRequestURI(), + ex.getEffectiveMessage() + ); + + return ResponseEntity + .status(errorCode.getHttpStatus()) + .body(errorResponse); + } + + //@Valid나 @Validated 검증 실패 시 발생하는 예외 처리 ex)이메일 형식이 잘못되었을 때, 비밀번호 최소 몇 자 이상 등 + @ExceptionHandler(MethodArgumentNotValidException.class) + public ResponseEntity handleValidationException( + MethodArgumentNotValidException ex, + HttpServletRequest request) { + + log.warn("Validation exception occurred: {}", ex.getMessage()); + + String errorMessage = ex.getBindingResult() + .getFieldErrors() + .stream() + .map(FieldError::getDefaultMessage) + .collect(Collectors.joining(", ")); + + ErrorResponseDTO errorResponse = ErrorResponseDTO.of( + ErrorCode.INVALID_INPUT, + request.getRequestURI(), + "입력값 검증 실패: " + errorMessage + ); + + return ResponseEntity + .status(ErrorCode.INVALID_INPUT.getHttpStatus()) + .body(errorResponse); + } + + //요청 데이터를 객체로 바인딩할 때 타입 불일치나 값 변환 실패가 발생하면 BindException이 발생. ex)검색어 누락 -> 검색어는 필수입니다. + @ExceptionHandler(BindException.class) + public ResponseEntity handleBindException( + BindException ex, + HttpServletRequest request) { + + log.warn("Bind exception occurred: {}", ex.getMessage()); + + String errorMessage = ex.getBindingResult() + .getFieldErrors() + .stream() + .map(FieldError::getDefaultMessage) + .collect(Collectors.joining(", ")); + + ErrorResponseDTO errorResponse = ErrorResponseDTO.of( + ErrorCode.INVALID_INPUT, + request.getRequestURI(), + "바인딩 오류: " + errorMessage + ); + + return ResponseEntity + .status(ErrorCode.INVALID_INPUT.getHttpStatus()) + .body(errorResponse); + } + + //필수 쿼리 파라미터가 요청에서 빠졌을 때 발생. ex) id 파라미터 누락 + @ExceptionHandler(MissingServletRequestParameterException.class) + public ResponseEntity handleMissingParameterException( + MissingServletRequestParameterException ex, + HttpServletRequest request) { + + log.warn("Missing parameter exception occurred: {}", ex.getMessage()); + + ErrorResponseDTO errorResponse = ErrorResponseDTO.of( + ErrorCode.MISSING_REQUIRED_FIELD, + request.getRequestURI(), + "필수 파라미터 누락: " + ex.getParameterName() + ); + + return ResponseEntity + .status(ErrorCode.MISSING_REQUIRED_FIELD.getHttpStatus()) + .body(errorResponse); + } + + //요청 파라미터 또는 경로 변수의 타입 변환 실패 시 발생. ex)id가 Long이어야 하는데 "abc" 전달 + @ExceptionHandler(MethodArgumentTypeMismatchException.class) + public ResponseEntity handleTypeMismatchException( + MethodArgumentTypeMismatchException ex, + HttpServletRequest request) { + + log.warn("Type mismatch exception occurred: {}", ex.getMessage()); + + ErrorResponseDTO errorResponse = ErrorResponseDTO.of( + ErrorCode.INVALID_FORMAT, + request.getRequestURI(), + "잘못된 파라미터 타입: " + ex.getName() + ); + + return ResponseEntity + .status(ErrorCode.INVALID_FORMAT.getHttpStatus()) + .body(errorResponse); + } + + //요청 바디(JSON 등)를 Spring이 읽거나 파싱할 수 없을 때 발생. ex) JSON 문법 오류, 잘못된 타입, 비어있는 본문 등. + @ExceptionHandler(HttpMessageNotReadableException.class) + public ResponseEntity handleHttpMessageNotReadableException( + HttpMessageNotReadableException ex, + HttpServletRequest request) { + + log.warn("Http message not readable exception occurred: {}", ex.getMessage()); + + ErrorResponseDTO errorResponse = ErrorResponseDTO.of( + ErrorCode.INVALID_FORMAT, + request.getRequestURI(), + "요청 본문을 읽을 수 없습니다." + ); + + return ResponseEntity + .status(ErrorCode.INVALID_FORMAT.getHttpStatus()) + .body(errorResponse); + } + + //지원하지 않는 HTTP 메서드를 호출할 때 발생. ex)GET 요청을 보냈지만 컨트롤러는 POST만 지원 + @ExceptionHandler(HttpRequestMethodNotSupportedException.class) + public ResponseEntity handleMethodNotSupportedException( + HttpRequestMethodNotSupportedException ex, + HttpServletRequest request) { + + log.warn("Method not supported exception occurred: {}", ex.getMessage()); + + ErrorResponseDTO errorResponse = ErrorResponseDTO.of( + ErrorCode.METHOD_NOT_ALLOWED, + request.getRequestURI() + ); + + return ResponseEntity + .status(ErrorCode.METHOD_NOT_ALLOWED.getHttpStatus()) + .body(errorResponse); + } + + //매핑된 컨트롤러(핸들러)가 없는 잘못된 URL 호출 시 발생. + @ExceptionHandler(NoHandlerFoundException.class) + public ResponseEntity handleNoHandlerFoundException( + NoHandlerFoundException ex, + HttpServletRequest request) { + + log.warn("No handler found exception occurred: {}", ex.getMessage()); + + ErrorResponseDTO errorResponse = ErrorResponseDTO.of( + ErrorCode.NOT_FOUND, + request.getRequestURI() + ); + + return ResponseEntity + .status(ErrorCode.NOT_FOUND.getHttpStatus()) + .body(errorResponse); + } + + //위에서 처리하지 못한 모든 예외를 처리하는 최후의 방어선. + @ExceptionHandler(Exception.class) + public ResponseEntity handleGeneralException( + Exception ex, + HttpServletRequest request) { + + log.error("Unexpected exception occurred: {}", ex.getMessage(), ex); + + ErrorResponseDTO errorResponse = ErrorResponseDTO.of( + ErrorCode.INTERNAL_SERVER_ERROR, + request.getRequestURI() + ); + + return ResponseEntity + .status(ErrorCode.INTERNAL_SERVER_ERROR.getHttpStatus()) + .body(errorResponse); + } +} \ No newline at end of file From f53f7aeb584170e4f93a2e0d7c368e86f24d552f Mon Sep 17 00:00:00 2001 From: JungSoonIn Date: Thu, 11 Sep 2025 22:05:05 +0900 Subject: [PATCH 17/20] =?UTF-8?q?test:=20=EC=98=88=EC=99=B8=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EB=8B=A8?= =?UTF-8?q?=EC=9C=84=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/response/ErrorResponseDTOTest.java | 109 +++++++++++++++ .../common/enums/ErrorCodeTest.java | 71 ++++++++++ .../exception/BusinessExceptionTest.java | 125 ++++++++++++++++++ 3 files changed, 305 insertions(+) create mode 100644 springboot/src/test/java/com/softlabs/aicontents/common/dto/response/ErrorResponseDTOTest.java create mode 100644 springboot/src/test/java/com/softlabs/aicontents/common/enums/ErrorCodeTest.java create mode 100644 springboot/src/test/java/com/softlabs/aicontents/common/exception/BusinessExceptionTest.java diff --git a/springboot/src/test/java/com/softlabs/aicontents/common/dto/response/ErrorResponseDTOTest.java b/springboot/src/test/java/com/softlabs/aicontents/common/dto/response/ErrorResponseDTOTest.java new file mode 100644 index 00000000..b1a3ea92 --- /dev/null +++ b/springboot/src/test/java/com/softlabs/aicontents/common/dto/response/ErrorResponseDTOTest.java @@ -0,0 +1,109 @@ +package com.softlabs.aicontents.common.dto.response; + +import com.softlabs.aicontents.common.enums.ErrorCode; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.time.LocalDateTime; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mockStatic; + +@ExtendWith(MockitoExtension.class) +class ErrorResponseDTOTest { + + @Test + void testErrorResponseCreationWithPath() { + String path = "/api/test"; + ErrorCode errorCode = ErrorCode.NOT_FOUND; + + ErrorResponseDTO response = ErrorResponseDTO.of(errorCode, path); + + assertThat(response.code()).isEqualTo(errorCode.getCode()); + assertThat(response.message()).isEqualTo(errorCode.getMessage()); + assertThat(response.path()).isEqualTo(path); + assertThat(response.status()).isEqualTo(errorCode.getStatus()); + assertThat(response.timestamp()).isNotNull(); + assertThat(response.traceId()).isNotNull(); + } + + @Test + void testErrorResponseCreationWithCustomMessage() { + String path = "/api/test"; + String customMessage = "사용자 정의 오류 메시지"; + ErrorCode errorCode = ErrorCode.INVALID_INPUT; + + ErrorResponseDTO response = ErrorResponseDTO.of(errorCode, path, customMessage); + + assertThat(response.code()).isEqualTo(errorCode.getCode()); + assertThat(response.message()).isEqualTo(customMessage); + assertThat(response.path()).isEqualTo(path); + assertThat(response.status()).isEqualTo(errorCode.getStatus()); + assertThat(response.timestamp()).isNotNull(); + assertThat(response.traceId()).isNotNull(); + } + + @Test + void testErrorResponseCreationWithCustomTraceId() { + String path = "/api/test"; + String customTraceId = "custom-trace-123"; + ErrorCode errorCode = ErrorCode.INTERNAL_SERVER_ERROR; + + ErrorResponseDTO response = ErrorResponseDTO.ofWithTraceId(errorCode, path, customTraceId); + + assertThat(response.code()).isEqualTo(errorCode.getCode()); + assertThat(response.message()).isEqualTo(errorCode.getMessage()); + assertThat(response.path()).isEqualTo(path); + assertThat(response.status()).isEqualTo(errorCode.getStatus()); + assertThat(response.timestamp()).isNotNull(); + assertThat(response.traceId()).isEqualTo(customTraceId); + } + + @Test + void testErrorResponseTimestamp() { + LocalDateTime beforeCreation = LocalDateTime.now().minusSeconds(1); + + ErrorResponseDTO response = ErrorResponseDTO.of(ErrorCode.BAD_REQUEST, "/api/test"); + + LocalDateTime afterCreation = LocalDateTime.now().plusSeconds(1); + + assertThat(response.timestamp()).isBetween(beforeCreation, afterCreation); + } + + @Test + void testErrorResponseWithDifferentErrorCodes() { + String path = "/api/test"; + + ErrorResponseDTO badRequestResponse = ErrorResponseDTO.of(ErrorCode.BAD_REQUEST, path); + assertThat(badRequestResponse.status()).isEqualTo(400); + assertThat(badRequestResponse.code()).isEqualTo("E001"); + + ErrorResponseDTO unauthorizedResponse = ErrorResponseDTO.of(ErrorCode.UNAUTHORIZED, path); + assertThat(unauthorizedResponse.status()).isEqualTo(401); + assertThat(unauthorizedResponse.code()).isEqualTo("E101"); + + ErrorResponseDTO notFoundResponse = ErrorResponseDTO.of(ErrorCode.NOT_FOUND, path); + assertThat(notFoundResponse.status()).isEqualTo(404); + assertThat(notFoundResponse.code()).isEqualTo("E301"); + + ErrorResponseDTO serverErrorResponse = ErrorResponseDTO.of(ErrorCode.INTERNAL_SERVER_ERROR, path); + assertThat(serverErrorResponse.status()).isEqualTo(500); + assertThat(serverErrorResponse.code()).isEqualTo("E901"); + } + + @Test + void testErrorResponseImmutability() { + ErrorCode errorCode = ErrorCode.FORBIDDEN; + String path = "/api/forbidden"; + + ErrorResponseDTO response = ErrorResponseDTO.of(errorCode, path); + + assertThat(response.code()).isEqualTo(errorCode.getCode()); + assertThat(response.message()).isEqualTo(errorCode.getMessage()); + assertThat(response.path()).isEqualTo(path); + assertThat(response.status()).isEqualTo(errorCode.getStatus()); + } +} \ No newline at end of file diff --git a/springboot/src/test/java/com/softlabs/aicontents/common/enums/ErrorCodeTest.java b/springboot/src/test/java/com/softlabs/aicontents/common/enums/ErrorCodeTest.java new file mode 100644 index 00000000..8f790fc2 --- /dev/null +++ b/springboot/src/test/java/com/softlabs/aicontents/common/enums/ErrorCodeTest.java @@ -0,0 +1,71 @@ +package com.softlabs.aicontents.common.enums; + +import org.junit.jupiter.api.Test; +import org.springframework.http.HttpStatus; + +import static org.assertj.core.api.Assertions.assertThat; + +class ErrorCodeTest { + + @Test + void testErrorCodeProperties() { + ErrorCode errorCode = ErrorCode.INVALID_INPUT; + + assertThat(errorCode.getHttpStatus()).isEqualTo(HttpStatus.BAD_REQUEST); + assertThat(errorCode.getCode()).isEqualTo("E002"); + assertThat(errorCode.getMessage()).isEqualTo("입력값이 올바르지 않습니다."); + assertThat(errorCode.getStatus()).isEqualTo(400); + } + + @Test + void testAllErrorCodesHaveValidProperties() { + for (ErrorCode errorCode : ErrorCode.values()) { + assertThat(errorCode.getHttpStatus()).isNotNull(); + assertThat(errorCode.getCode()).isNotEmpty(); + assertThat(errorCode.getMessage()).isNotEmpty(); + assertThat(errorCode.getStatus()).isGreaterThan(0); + } + } + + @Test + void testClientErrorCodes() { + assertThat(ErrorCode.BAD_REQUEST.getStatus()).isEqualTo(400); + assertThat(ErrorCode.UNAUTHORIZED.getStatus()).isEqualTo(401); + assertThat(ErrorCode.FORBIDDEN.getStatus()).isEqualTo(403); + assertThat(ErrorCode.NOT_FOUND.getStatus()).isEqualTo(404); + assertThat(ErrorCode.METHOD_NOT_ALLOWED.getStatus()).isEqualTo(405); + assertThat(ErrorCode.CONFLICT.getStatus()).isEqualTo(409); + } + + @Test + void testServerErrorCodes() { + assertThat(ErrorCode.INTERNAL_SERVER_ERROR.getStatus()).isEqualTo(500); + assertThat(ErrorCode.SERVICE_UNAVAILABLE.getStatus()).isEqualTo(503); + } + + @Test + void testErrorCodeUniqueness() { + ErrorCode[] errorCodes = ErrorCode.values(); + + for (int i = 0; i < errorCodes.length; i++) { + for (int j = i + 1; j < errorCodes.length; j++) { + assertThat(errorCodes[i].getCode()) + .as("Error codes should be unique: %s vs %s", errorCodes[i], errorCodes[j]) + .isNotEqualTo(errorCodes[j].getCode()); + } + } + } + + @Test + void testSpecificErrorCodes() { + ErrorCode notFound = ErrorCode.NOT_FOUND; + assertThat(notFound.getCode()).isEqualTo("E301"); + assertThat(notFound.getMessage()).isEqualTo("요청한 리소스를 찾을 수 없습니다."); + assertThat(notFound.getHttpStatus()).isEqualTo(HttpStatus.NOT_FOUND); + + ErrorCode internalError = ErrorCode.INTERNAL_SERVER_ERROR; + assertThat(internalError.getCode()).isEqualTo("E901"); + assertThat(internalError.getMessage()).isEqualTo("내부 서버 오류가 발생했습니다."); + assertThat(internalError.getHttpStatus()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR); + } +} \ No newline at end of file diff --git a/springboot/src/test/java/com/softlabs/aicontents/common/exception/BusinessExceptionTest.java b/springboot/src/test/java/com/softlabs/aicontents/common/exception/BusinessExceptionTest.java new file mode 100644 index 00000000..7ce74f9c --- /dev/null +++ b/springboot/src/test/java/com/softlabs/aicontents/common/exception/BusinessExceptionTest.java @@ -0,0 +1,125 @@ +package com.softlabs.aicontents.common.exception; + +import com.softlabs.aicontents.common.enums.ErrorCode; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class BusinessExceptionTest { + + @Test + void testBusinessExceptionWithErrorCode() { + ErrorCode errorCode = ErrorCode.NOT_FOUND; + + BusinessException exception = new BusinessException(errorCode); + + assertThat(exception.getErrorCode()).isEqualTo(errorCode); + assertThat(exception.getMessage()).isEqualTo(errorCode.getMessage()); + assertThat(exception.getCustomMessage()).isNull(); + assertThat(exception.getEffectiveMessage()).isEqualTo(errorCode.getMessage()); + } + + @Test + void testBusinessExceptionWithCustomMessage() { + ErrorCode errorCode = ErrorCode.INVALID_INPUT; + String customMessage = "사용자 정의 오류 메시지"; + + BusinessException exception = new BusinessException(errorCode, customMessage); + + assertThat(exception.getErrorCode()).isEqualTo(errorCode); + assertThat(exception.getMessage()).isEqualTo(customMessage); + assertThat(exception.getCustomMessage()).isEqualTo(customMessage); + assertThat(exception.getEffectiveMessage()).isEqualTo(customMessage); + } + + @Test + void testBusinessExceptionWithCause() { + ErrorCode errorCode = ErrorCode.INTERNAL_SERVER_ERROR; + Throwable cause = new RuntimeException("원인 예외"); + + BusinessException exception = new BusinessException(errorCode, cause); + + assertThat(exception.getErrorCode()).isEqualTo(errorCode); + assertThat(exception.getMessage()).isEqualTo(errorCode.getMessage()); + assertThat(exception.getCause()).isEqualTo(cause); + assertThat(exception.getCustomMessage()).isNull(); + assertThat(exception.getEffectiveMessage()).isEqualTo(errorCode.getMessage()); + } + + @Test + void testBusinessExceptionWithCustomMessageAndCause() { + ErrorCode errorCode = ErrorCode.DATABASE_ERROR; + String customMessage = "데이터베이스 연결 실패"; + Throwable cause = new RuntimeException("Connection timeout"); + + BusinessException exception = new BusinessException(errorCode, customMessage, cause); + + assertThat(exception.getErrorCode()).isEqualTo(errorCode); + assertThat(exception.getMessage()).isEqualTo(customMessage); + assertThat(exception.getCause()).isEqualTo(cause); + assertThat(exception.getCustomMessage()).isEqualTo(customMessage); + assertThat(exception.getEffectiveMessage()).isEqualTo(customMessage); + } + + @Test + void testEffectiveMessageWithoutCustomMessage() { + ErrorCode errorCode = ErrorCode.FORBIDDEN; + BusinessException exception = new BusinessException(errorCode); + + assertThat(exception.getEffectiveMessage()).isEqualTo(errorCode.getMessage()); + } + + @Test + void testEffectiveMessageWithCustomMessage() { + ErrorCode errorCode = ErrorCode.FORBIDDEN; + String customMessage = "특정 리소스에 대한 접근 권한이 없습니다"; + BusinessException exception = new BusinessException(errorCode, customMessage); + + assertThat(exception.getEffectiveMessage()).isEqualTo(customMessage); + } + + @Test + void testBusinessExceptionInheritance() { + ErrorCode errorCode = ErrorCode.BAD_REQUEST; + BusinessException exception = new BusinessException(errorCode); + + assertThat(exception).isInstanceOf(RuntimeException.class); + assertThat(exception).isInstanceOf(Exception.class); + assertThat(exception).isInstanceOf(Throwable.class); + } + + @Test + void testBusinessExceptionWithDifferentErrorCodes() { + BusinessException badRequestException = new BusinessException(ErrorCode.BAD_REQUEST); + assertThat(badRequestException.getErrorCode()).isEqualTo(ErrorCode.BAD_REQUEST); + + BusinessException unauthorizedException = new BusinessException(ErrorCode.UNAUTHORIZED); + assertThat(unauthorizedException.getErrorCode()).isEqualTo(ErrorCode.UNAUTHORIZED); + + BusinessException notFoundException = new BusinessException(ErrorCode.NOT_FOUND); + assertThat(notFoundException.getErrorCode()).isEqualTo(ErrorCode.NOT_FOUND); + + BusinessException serverErrorException = new BusinessException(ErrorCode.INTERNAL_SERVER_ERROR); + assertThat(serverErrorException.getErrorCode()).isEqualTo(ErrorCode.INTERNAL_SERVER_ERROR); + } + + @Test + void testBusinessExceptionMessageConsistency() { + ErrorCode errorCode = ErrorCode.CONFLICT; + BusinessException exception = new BusinessException(errorCode); + + assertThat(exception.getMessage()).isEqualTo(errorCode.getMessage()); + assertThat(exception.getEffectiveMessage()).isEqualTo(errorCode.getMessage()); + } + + @Test + void testCustomMessageOverridesDefaultMessage() { + ErrorCode errorCode = ErrorCode.SERVICE_UNAVAILABLE; + String customMessage = "현재 시스템 점검 중입니다"; + BusinessException exception = new BusinessException(errorCode, customMessage); + + assertThat(exception.getMessage()).isEqualTo(customMessage); + assertThat(exception.getEffectiveMessage()).isEqualTo(customMessage); + assertThat(exception.getMessage()).isNotEqualTo(errorCode.getMessage()); + } +} \ No newline at end of file From d75dc680cbb71224ea8218ab86d0faff9ed91b85 Mon Sep 17 00:00:00 2001 From: JungSoonIn Date: Thu, 11 Sep 2025 22:14:09 +0900 Subject: [PATCH 18/20] chore: spotlessApply --- .../common/dto/response/ErrorResponseDTO.java | 74 ++--- .../aicontents/common/enums/ErrorCode.java | 110 +++--- .../common/exception/BusinessException.java | 115 ++++--- .../exception/GlobalExceptionHandler.java | 314 ++++++++---------- .../dto/response/ErrorResponseDTOTest.java | 197 ++++++----- .../common/enums/ErrorCodeTest.java | 126 +++---- .../exception/BusinessExceptionTest.java | 238 ++++++------- 7 files changed, 557 insertions(+), 617 deletions(-) diff --git a/springboot/src/main/java/com/softlabs/aicontents/common/dto/response/ErrorResponseDTO.java b/springboot/src/main/java/com/softlabs/aicontents/common/dto/response/ErrorResponseDTO.java index c37609ac..d13ddea5 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/common/dto/response/ErrorResponseDTO.java +++ b/springboot/src/main/java/com/softlabs/aicontents/common/dto/response/ErrorResponseDTO.java @@ -2,48 +2,38 @@ import com.softlabs.aicontents.common.enums.ErrorCode; import com.softlabs.aicontents.common.util.TraceIdUtil; - import java.time.LocalDateTime; public record ErrorResponseDTO( - LocalDateTime timestamp, - String code, - String message, - String traceId, - String path, - int status -) { - - public static ErrorResponseDTO of(ErrorCode errorCode, String path) { - return new ErrorResponseDTO( - LocalDateTime.now(), - errorCode.getCode(), - errorCode.getMessage(), - TraceIdUtil.getOrCreateTraceId(), - path, - errorCode.getStatus() - ); - } - - public static ErrorResponseDTO of(ErrorCode errorCode, String path, String customMessage) { - return new ErrorResponseDTO( - LocalDateTime.now(), - errorCode.getCode(), - customMessage, - TraceIdUtil.getOrCreateTraceId(), - path, - errorCode.getStatus() - ); - } - - public static ErrorResponseDTO ofWithTraceId(ErrorCode errorCode, String path, String traceId) { - return new ErrorResponseDTO( - LocalDateTime.now(), - errorCode.getCode(), - errorCode.getMessage(), - traceId, - path, - errorCode.getStatus() - ); - } -} \ No newline at end of file + LocalDateTime timestamp, String code, String message, String traceId, String path, int status) { + + public static ErrorResponseDTO of(ErrorCode errorCode, String path) { + return new ErrorResponseDTO( + LocalDateTime.now(), + errorCode.getCode(), + errorCode.getMessage(), + TraceIdUtil.getOrCreateTraceId(), + path, + errorCode.getStatus()); + } + + public static ErrorResponseDTO of(ErrorCode errorCode, String path, String customMessage) { + return new ErrorResponseDTO( + LocalDateTime.now(), + errorCode.getCode(), + customMessage, + TraceIdUtil.getOrCreateTraceId(), + path, + errorCode.getStatus()); + } + + public static ErrorResponseDTO ofWithTraceId(ErrorCode errorCode, String path, String traceId) { + return new ErrorResponseDTO( + LocalDateTime.now(), + errorCode.getCode(), + errorCode.getMessage(), + traceId, + path, + errorCode.getStatus()); + } +} diff --git a/springboot/src/main/java/com/softlabs/aicontents/common/enums/ErrorCode.java b/springboot/src/main/java/com/softlabs/aicontents/common/enums/ErrorCode.java index 8eb5de8b..3e22d286 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/common/enums/ErrorCode.java +++ b/springboot/src/main/java/com/softlabs/aicontents/common/enums/ErrorCode.java @@ -3,58 +3,58 @@ import org.springframework.http.HttpStatus; public enum ErrorCode { - - // 400번대 클라이언트 에러 - BAD_REQUEST(HttpStatus.BAD_REQUEST, "E001", "잘못된 요청입니다."), - INVALID_INPUT(HttpStatus.BAD_REQUEST, "E002", "입력값이 올바르지 않습니다."), - MISSING_REQUIRED_FIELD(HttpStatus.BAD_REQUEST, "E003", "필수 필드가 누락되었습니다."), - INVALID_FORMAT(HttpStatus.BAD_REQUEST, "E004", "올바르지 않은 형식입니다."), - - UNAUTHORIZED(HttpStatus.UNAUTHORIZED, "E101", "인증이 필요합니다."), - INVALID_TOKEN(HttpStatus.UNAUTHORIZED, "E102", "유효하지 않은 토큰입니다."), - TOKEN_EXPIRED(HttpStatus.UNAUTHORIZED, "E103", "토큰이 만료되었습니다."), - - FORBIDDEN(HttpStatus.FORBIDDEN, "E201", "접근 권한이 없습니다."), - INSUFFICIENT_PERMISSIONS(HttpStatus.FORBIDDEN, "E202", "권한이 부족합니다."), - - NOT_FOUND(HttpStatus.NOT_FOUND, "E301", "요청한 리소스를 찾을 수 없습니다."), - USER_NOT_FOUND(HttpStatus.NOT_FOUND, "E302", "사용자를 찾을 수 없습니다."), - DATA_NOT_FOUND(HttpStatus.NOT_FOUND, "E303", "데이터를 찾을 수 없습니다."), - - METHOD_NOT_ALLOWED(HttpStatus.METHOD_NOT_ALLOWED, "E401", "허용되지 않은 HTTP 메서드입니다."), - - CONFLICT(HttpStatus.CONFLICT, "E501", "데이터 충돌이 발생했습니다."), - DUPLICATE_RESOURCE(HttpStatus.CONFLICT, "E502", "이미 존재하는 리소스입니다."), - - // 500번대 서버 에러 - INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "E901", "내부 서버 오류가 발생했습니다."), - DATABASE_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "E902", "데이터베이스 오류가 발생했습니다."), - EXTERNAL_API_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "E903", "외부 API 호출 중 오류가 발생했습니다."), - SERVICE_UNAVAILABLE(HttpStatus.SERVICE_UNAVAILABLE, "E904", "서비스를 사용할 수 없습니다."); - - private final HttpStatus httpStatus; - private final String code; - private final String message; - - ErrorCode(HttpStatus httpStatus, String code, String message) { - this.httpStatus = httpStatus; - this.code = code; - this.message = message; - } - - public HttpStatus getHttpStatus() { - return httpStatus; - } - - public String getCode() { - return code; - } - - public String getMessage() { - return message; - } - - public int getStatus() { - return httpStatus.value(); - } -} \ No newline at end of file + + // 400번대 클라이언트 에러 + BAD_REQUEST(HttpStatus.BAD_REQUEST, "E001", "잘못된 요청입니다."), + INVALID_INPUT(HttpStatus.BAD_REQUEST, "E002", "입력값이 올바르지 않습니다."), + MISSING_REQUIRED_FIELD(HttpStatus.BAD_REQUEST, "E003", "필수 필드가 누락되었습니다."), + INVALID_FORMAT(HttpStatus.BAD_REQUEST, "E004", "올바르지 않은 형식입니다."), + + UNAUTHORIZED(HttpStatus.UNAUTHORIZED, "E101", "인증이 필요합니다."), + INVALID_TOKEN(HttpStatus.UNAUTHORIZED, "E102", "유효하지 않은 토큰입니다."), + TOKEN_EXPIRED(HttpStatus.UNAUTHORIZED, "E103", "토큰이 만료되었습니다."), + + FORBIDDEN(HttpStatus.FORBIDDEN, "E201", "접근 권한이 없습니다."), + INSUFFICIENT_PERMISSIONS(HttpStatus.FORBIDDEN, "E202", "권한이 부족합니다."), + + NOT_FOUND(HttpStatus.NOT_FOUND, "E301", "요청한 리소스를 찾을 수 없습니다."), + USER_NOT_FOUND(HttpStatus.NOT_FOUND, "E302", "사용자를 찾을 수 없습니다."), + DATA_NOT_FOUND(HttpStatus.NOT_FOUND, "E303", "데이터를 찾을 수 없습니다."), + + METHOD_NOT_ALLOWED(HttpStatus.METHOD_NOT_ALLOWED, "E401", "허용되지 않은 HTTP 메서드입니다."), + + CONFLICT(HttpStatus.CONFLICT, "E501", "데이터 충돌이 발생했습니다."), + DUPLICATE_RESOURCE(HttpStatus.CONFLICT, "E502", "이미 존재하는 리소스입니다."), + + // 500번대 서버 에러 + INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "E901", "내부 서버 오류가 발생했습니다."), + DATABASE_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "E902", "데이터베이스 오류가 발생했습니다."), + EXTERNAL_API_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "E903", "외부 API 호출 중 오류가 발생했습니다."), + SERVICE_UNAVAILABLE(HttpStatus.SERVICE_UNAVAILABLE, "E904", "서비스를 사용할 수 없습니다."); + + private final HttpStatus httpStatus; + private final String code; + private final String message; + + ErrorCode(HttpStatus httpStatus, String code, String message) { + this.httpStatus = httpStatus; + this.code = code; + this.message = message; + } + + public HttpStatus getHttpStatus() { + return httpStatus; + } + + public String getCode() { + return code; + } + + public String getMessage() { + return message; + } + + public int getStatus() { + return httpStatus.value(); + } +} diff --git a/springboot/src/main/java/com/softlabs/aicontents/common/exception/BusinessException.java b/springboot/src/main/java/com/softlabs/aicontents/common/exception/BusinessException.java index ace67e19..e3791864 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/common/exception/BusinessException.java +++ b/springboot/src/main/java/com/softlabs/aicontents/common/exception/BusinessException.java @@ -3,66 +3,65 @@ import com.softlabs.aicontents.common.enums.ErrorCode; public class BusinessException extends RuntimeException { - - private final ErrorCode errorCode; - private final String customMessage; - //기본 생성자 (ErrorCode만 사용) - //사용 예시 throw new BusinessException(ErrorCode.USER_NOT_FOUND); - public BusinessException(ErrorCode errorCode) { - super(errorCode.getMessage()); - this.errorCode = errorCode; - this.customMessage = null; - } + private final ErrorCode errorCode; + private final String customMessage; - //커스텀 메시지 사용 가능 - //사용 예시 throw new BusinessException(ErrorCode.INVALID_INPUT, "이메일 형식이 잘못되었습니다."); - public BusinessException(ErrorCode errorCode, String customMessage) { - super(customMessage); - this.errorCode = errorCode; - this.customMessage = customMessage; - } + // 기본 생성자 (ErrorCode만 사용) + // 사용 예시 throw new BusinessException(ErrorCode.USER_NOT_FOUND); + public BusinessException(ErrorCode errorCode) { + super(errorCode.getMessage()); + this.errorCode = errorCode; + this.customMessage = null; + } - //원인 예외(cause) 전달 가능 - //사용 예시 - // try { - // externalApi.call(); - //} catch (IOException e) { - // throw new BusinessException(ErrorCode.EXTERNAL_API_ERROR, e); - //} - public BusinessException(ErrorCode errorCode, Throwable cause) { - super(errorCode.getMessage(), cause); - this.errorCode = errorCode; - this.customMessage = null; - } + // 커스텀 메시지 사용 가능 + // 사용 예시 throw new BusinessException(ErrorCode.INVALID_INPUT, "이메일 형식이 잘못되었습니다."); + public BusinessException(ErrorCode errorCode, String customMessage) { + super(customMessage); + this.errorCode = errorCode; + this.customMessage = customMessage; + } + // 원인 예외(cause) 전달 가능 + // 사용 예시 + // try { + // externalApi.call(); + // } catch (IOException e) { + // throw new BusinessException(ErrorCode.EXTERNAL_API_ERROR, e); + // } + public BusinessException(ErrorCode errorCode, Throwable cause) { + super(errorCode.getMessage(), cause); + this.errorCode = errorCode; + this.customMessage = null; + } - //커스텀 메시지 + 원인 예외 둘 다 사용 - //사용 예시 - //try { - // paymentGateway.call(); - //} catch (Exception e) { - // throw new BusinessException( - // ErrorCode.EXTERNAL_API_ERROR, - // "결제 서비스 호출 중 오류 발생", - // e - // ); - //} - public BusinessException(ErrorCode errorCode, String customMessage, Throwable cause) { - super(customMessage, cause); - this.errorCode = errorCode; - this.customMessage = customMessage; - } - - public ErrorCode getErrorCode() { - return errorCode; - } - - public String getCustomMessage() { - return customMessage; - } - - public String getEffectiveMessage() { - return customMessage != null ? customMessage : errorCode.getMessage(); - } -} \ No newline at end of file + // 커스텀 메시지 + 원인 예외 둘 다 사용 + // 사용 예시 + // try { + // paymentGateway.call(); + // } catch (Exception e) { + // throw new BusinessException( + // ErrorCode.EXTERNAL_API_ERROR, + // "결제 서비스 호출 중 오류 발생", + // e + // ); + // } + public BusinessException(ErrorCode errorCode, String customMessage, Throwable cause) { + super(customMessage, cause); + this.errorCode = errorCode; + this.customMessage = customMessage; + } + + public ErrorCode getErrorCode() { + return errorCode; + } + + public String getCustomMessage() { + return customMessage; + } + + public String getEffectiveMessage() { + return customMessage != null ? customMessage : errorCode.getMessage(); + } +} diff --git a/springboot/src/main/java/com/softlabs/aicontents/common/exception/GlobalExceptionHandler.java b/springboot/src/main/java/com/softlabs/aicontents/common/exception/GlobalExceptionHandler.java index 9c1a7283..c73833f0 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/common/exception/GlobalExceptionHandler.java +++ b/springboot/src/main/java/com/softlabs/aicontents/common/exception/GlobalExceptionHandler.java @@ -3,6 +3,7 @@ import com.softlabs.aicontents.common.dto.response.ErrorResponseDTO; import com.softlabs.aicontents.common.enums.ErrorCode; import jakarta.servlet.http.HttpServletRequest; +import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.ResponseEntity; @@ -17,191 +18,144 @@ import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; import org.springframework.web.servlet.NoHandlerFoundException; -import java.util.stream.Collectors; - @RestControllerAdvice public class GlobalExceptionHandler { - - private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class); - - //BusinessException에 정의된 예외 처리 - @ExceptionHandler(BusinessException.class) - public ResponseEntity handleBusinessException( - BusinessException ex, - HttpServletRequest request) { - - log.warn("Business exception occurred: {}", ex.getMessage(), ex); - - ErrorCode errorCode = ex.getErrorCode(); - ErrorResponseDTO errorResponse = ErrorResponseDTO.of( - errorCode, - request.getRequestURI(), - ex.getEffectiveMessage() - ); - - return ResponseEntity - .status(errorCode.getHttpStatus()) - .body(errorResponse); - } - - //@Valid나 @Validated 검증 실패 시 발생하는 예외 처리 ex)이메일 형식이 잘못되었을 때, 비밀번호 최소 몇 자 이상 등 - @ExceptionHandler(MethodArgumentNotValidException.class) - public ResponseEntity handleValidationException( - MethodArgumentNotValidException ex, - HttpServletRequest request) { - - log.warn("Validation exception occurred: {}", ex.getMessage()); - - String errorMessage = ex.getBindingResult() - .getFieldErrors() - .stream() + + private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class); + + // BusinessException에 정의된 예외 처리 + @ExceptionHandler(BusinessException.class) + public ResponseEntity handleBusinessException( + BusinessException ex, HttpServletRequest request) { + + log.warn("Business exception occurred: {}", ex.getMessage(), ex); + + ErrorCode errorCode = ex.getErrorCode(); + ErrorResponseDTO errorResponse = + ErrorResponseDTO.of(errorCode, request.getRequestURI(), ex.getEffectiveMessage()); + + return ResponseEntity.status(errorCode.getHttpStatus()).body(errorResponse); + } + + // @Valid나 @Validated 검증 실패 시 발생하는 예외 처리 ex)이메일 형식이 잘못되었을 때, 비밀번호 최소 몇 자 이상 등 + @ExceptionHandler(MethodArgumentNotValidException.class) + public ResponseEntity handleValidationException( + MethodArgumentNotValidException ex, HttpServletRequest request) { + + log.warn("Validation exception occurred: {}", ex.getMessage()); + + String errorMessage = + ex.getBindingResult().getFieldErrors().stream() .map(FieldError::getDefaultMessage) .collect(Collectors.joining(", ")); - - ErrorResponseDTO errorResponse = ErrorResponseDTO.of( - ErrorCode.INVALID_INPUT, - request.getRequestURI(), - "입력값 검증 실패: " + errorMessage - ); - - return ResponseEntity - .status(ErrorCode.INVALID_INPUT.getHttpStatus()) - .body(errorResponse); - } - - //요청 데이터를 객체로 바인딩할 때 타입 불일치나 값 변환 실패가 발생하면 BindException이 발생. ex)검색어 누락 -> 검색어는 필수입니다. - @ExceptionHandler(BindException.class) - public ResponseEntity handleBindException( - BindException ex, - HttpServletRequest request) { - - log.warn("Bind exception occurred: {}", ex.getMessage()); - - String errorMessage = ex.getBindingResult() - .getFieldErrors() - .stream() + + ErrorResponseDTO errorResponse = + ErrorResponseDTO.of( + ErrorCode.INVALID_INPUT, request.getRequestURI(), "입력값 검증 실패: " + errorMessage); + + return ResponseEntity.status(ErrorCode.INVALID_INPUT.getHttpStatus()).body(errorResponse); + } + + // 요청 데이터를 객체로 바인딩할 때 타입 불일치나 값 변환 실패가 발생하면 BindException이 발생. ex)검색어 누락 -> 검색어는 필수입니다. + @ExceptionHandler(BindException.class) + public ResponseEntity handleBindException( + BindException ex, HttpServletRequest request) { + + log.warn("Bind exception occurred: {}", ex.getMessage()); + + String errorMessage = + ex.getBindingResult().getFieldErrors().stream() .map(FieldError::getDefaultMessage) .collect(Collectors.joining(", ")); - - ErrorResponseDTO errorResponse = ErrorResponseDTO.of( - ErrorCode.INVALID_INPUT, - request.getRequestURI(), - "바인딩 오류: " + errorMessage - ); - - return ResponseEntity - .status(ErrorCode.INVALID_INPUT.getHttpStatus()) - .body(errorResponse); - } - - //필수 쿼리 파라미터가 요청에서 빠졌을 때 발생. ex) id 파라미터 누락 - @ExceptionHandler(MissingServletRequestParameterException.class) - public ResponseEntity handleMissingParameterException( - MissingServletRequestParameterException ex, - HttpServletRequest request) { - - log.warn("Missing parameter exception occurred: {}", ex.getMessage()); - - ErrorResponseDTO errorResponse = ErrorResponseDTO.of( + + ErrorResponseDTO errorResponse = + ErrorResponseDTO.of( + ErrorCode.INVALID_INPUT, request.getRequestURI(), "바인딩 오류: " + errorMessage); + + return ResponseEntity.status(ErrorCode.INVALID_INPUT.getHttpStatus()).body(errorResponse); + } + + // 필수 쿼리 파라미터가 요청에서 빠졌을 때 발생. ex) id 파라미터 누락 + @ExceptionHandler(MissingServletRequestParameterException.class) + public ResponseEntity handleMissingParameterException( + MissingServletRequestParameterException ex, HttpServletRequest request) { + + log.warn("Missing parameter exception occurred: {}", ex.getMessage()); + + ErrorResponseDTO errorResponse = + ErrorResponseDTO.of( ErrorCode.MISSING_REQUIRED_FIELD, request.getRequestURI(), - "필수 파라미터 누락: " + ex.getParameterName() - ); - - return ResponseEntity - .status(ErrorCode.MISSING_REQUIRED_FIELD.getHttpStatus()) - .body(errorResponse); - } - - //요청 파라미터 또는 경로 변수의 타입 변환 실패 시 발생. ex)id가 Long이어야 하는데 "abc" 전달 - @ExceptionHandler(MethodArgumentTypeMismatchException.class) - public ResponseEntity handleTypeMismatchException( - MethodArgumentTypeMismatchException ex, - HttpServletRequest request) { - - log.warn("Type mismatch exception occurred: {}", ex.getMessage()); - - ErrorResponseDTO errorResponse = ErrorResponseDTO.of( - ErrorCode.INVALID_FORMAT, - request.getRequestURI(), - "잘못된 파라미터 타입: " + ex.getName() - ); - - return ResponseEntity - .status(ErrorCode.INVALID_FORMAT.getHttpStatus()) - .body(errorResponse); - } - - //요청 바디(JSON 등)를 Spring이 읽거나 파싱할 수 없을 때 발생. ex) JSON 문법 오류, 잘못된 타입, 비어있는 본문 등. - @ExceptionHandler(HttpMessageNotReadableException.class) - public ResponseEntity handleHttpMessageNotReadableException( - HttpMessageNotReadableException ex, - HttpServletRequest request) { - - log.warn("Http message not readable exception occurred: {}", ex.getMessage()); - - ErrorResponseDTO errorResponse = ErrorResponseDTO.of( - ErrorCode.INVALID_FORMAT, - request.getRequestURI(), - "요청 본문을 읽을 수 없습니다." - ); - - return ResponseEntity - .status(ErrorCode.INVALID_FORMAT.getHttpStatus()) - .body(errorResponse); - } - - //지원하지 않는 HTTP 메서드를 호출할 때 발생. ex)GET 요청을 보냈지만 컨트롤러는 POST만 지원 - @ExceptionHandler(HttpRequestMethodNotSupportedException.class) - public ResponseEntity handleMethodNotSupportedException( - HttpRequestMethodNotSupportedException ex, - HttpServletRequest request) { - - log.warn("Method not supported exception occurred: {}", ex.getMessage()); - - ErrorResponseDTO errorResponse = ErrorResponseDTO.of( - ErrorCode.METHOD_NOT_ALLOWED, - request.getRequestURI() - ); - - return ResponseEntity - .status(ErrorCode.METHOD_NOT_ALLOWED.getHttpStatus()) - .body(errorResponse); - } - - //매핑된 컨트롤러(핸들러)가 없는 잘못된 URL 호출 시 발생. - @ExceptionHandler(NoHandlerFoundException.class) - public ResponseEntity handleNoHandlerFoundException( - NoHandlerFoundException ex, - HttpServletRequest request) { - - log.warn("No handler found exception occurred: {}", ex.getMessage()); - - ErrorResponseDTO errorResponse = ErrorResponseDTO.of( - ErrorCode.NOT_FOUND, - request.getRequestURI() - ); - - return ResponseEntity - .status(ErrorCode.NOT_FOUND.getHttpStatus()) - .body(errorResponse); - } - - //위에서 처리하지 못한 모든 예외를 처리하는 최후의 방어선. - @ExceptionHandler(Exception.class) - public ResponseEntity handleGeneralException( - Exception ex, - HttpServletRequest request) { - - log.error("Unexpected exception occurred: {}", ex.getMessage(), ex); - - ErrorResponseDTO errorResponse = ErrorResponseDTO.of( - ErrorCode.INTERNAL_SERVER_ERROR, - request.getRequestURI() - ); - - return ResponseEntity - .status(ErrorCode.INTERNAL_SERVER_ERROR.getHttpStatus()) - .body(errorResponse); - } -} \ No newline at end of file + "필수 파라미터 누락: " + ex.getParameterName()); + + return ResponseEntity.status(ErrorCode.MISSING_REQUIRED_FIELD.getHttpStatus()) + .body(errorResponse); + } + + // 요청 파라미터 또는 경로 변수의 타입 변환 실패 시 발생. ex)id가 Long이어야 하는데 "abc" 전달 + @ExceptionHandler(MethodArgumentTypeMismatchException.class) + public ResponseEntity handleTypeMismatchException( + MethodArgumentTypeMismatchException ex, HttpServletRequest request) { + + log.warn("Type mismatch exception occurred: {}", ex.getMessage()); + + ErrorResponseDTO errorResponse = + ErrorResponseDTO.of( + ErrorCode.INVALID_FORMAT, request.getRequestURI(), "잘못된 파라미터 타입: " + ex.getName()); + + return ResponseEntity.status(ErrorCode.INVALID_FORMAT.getHttpStatus()).body(errorResponse); + } + + // 요청 바디(JSON 등)를 Spring이 읽거나 파싱할 수 없을 때 발생. ex) JSON 문법 오류, 잘못된 타입, 비어있는 본문 등. + @ExceptionHandler(HttpMessageNotReadableException.class) + public ResponseEntity handleHttpMessageNotReadableException( + HttpMessageNotReadableException ex, HttpServletRequest request) { + + log.warn("Http message not readable exception occurred: {}", ex.getMessage()); + + ErrorResponseDTO errorResponse = + ErrorResponseDTO.of(ErrorCode.INVALID_FORMAT, request.getRequestURI(), "요청 본문을 읽을 수 없습니다."); + + return ResponseEntity.status(ErrorCode.INVALID_FORMAT.getHttpStatus()).body(errorResponse); + } + + // 지원하지 않는 HTTP 메서드를 호출할 때 발생. ex)GET 요청을 보냈지만 컨트롤러는 POST만 지원 + @ExceptionHandler(HttpRequestMethodNotSupportedException.class) + public ResponseEntity handleMethodNotSupportedException( + HttpRequestMethodNotSupportedException ex, HttpServletRequest request) { + + log.warn("Method not supported exception occurred: {}", ex.getMessage()); + + ErrorResponseDTO errorResponse = + ErrorResponseDTO.of(ErrorCode.METHOD_NOT_ALLOWED, request.getRequestURI()); + + return ResponseEntity.status(ErrorCode.METHOD_NOT_ALLOWED.getHttpStatus()).body(errorResponse); + } + + // 매핑된 컨트롤러(핸들러)가 없는 잘못된 URL 호출 시 발생. + @ExceptionHandler(NoHandlerFoundException.class) + public ResponseEntity handleNoHandlerFoundException( + NoHandlerFoundException ex, HttpServletRequest request) { + + log.warn("No handler found exception occurred: {}", ex.getMessage()); + + ErrorResponseDTO errorResponse = + ErrorResponseDTO.of(ErrorCode.NOT_FOUND, request.getRequestURI()); + + return ResponseEntity.status(ErrorCode.NOT_FOUND.getHttpStatus()).body(errorResponse); + } + + // 위에서 처리하지 못한 모든 예외를 처리하는 최후의 방어선. + @ExceptionHandler(Exception.class) + public ResponseEntity handleGeneralException( + Exception ex, HttpServletRequest request) { + + log.error("Unexpected exception occurred: {}", ex.getMessage(), ex); + + ErrorResponseDTO errorResponse = + ErrorResponseDTO.of(ErrorCode.INTERNAL_SERVER_ERROR, request.getRequestURI()); + + return ResponseEntity.status(ErrorCode.INTERNAL_SERVER_ERROR.getHttpStatus()) + .body(errorResponse); + } +} diff --git a/springboot/src/test/java/com/softlabs/aicontents/common/dto/response/ErrorResponseDTOTest.java b/springboot/src/test/java/com/softlabs/aicontents/common/dto/response/ErrorResponseDTOTest.java index b1a3ea92..567b3d67 100644 --- a/springboot/src/test/java/com/softlabs/aicontents/common/dto/response/ErrorResponseDTOTest.java +++ b/springboot/src/test/java/com/softlabs/aicontents/common/dto/response/ErrorResponseDTOTest.java @@ -1,109 +1,106 @@ package com.softlabs.aicontents.common.dto.response; +import static org.assertj.core.api.Assertions.assertThat; + import com.softlabs.aicontents.common.enums.ErrorCode; +import java.time.LocalDateTime; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.MockedStatic; -import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; -import java.time.LocalDateTime; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mockStatic; - @ExtendWith(MockitoExtension.class) class ErrorResponseDTOTest { - - @Test - void testErrorResponseCreationWithPath() { - String path = "/api/test"; - ErrorCode errorCode = ErrorCode.NOT_FOUND; - - ErrorResponseDTO response = ErrorResponseDTO.of(errorCode, path); - - assertThat(response.code()).isEqualTo(errorCode.getCode()); - assertThat(response.message()).isEqualTo(errorCode.getMessage()); - assertThat(response.path()).isEqualTo(path); - assertThat(response.status()).isEqualTo(errorCode.getStatus()); - assertThat(response.timestamp()).isNotNull(); - assertThat(response.traceId()).isNotNull(); - } - - @Test - void testErrorResponseCreationWithCustomMessage() { - String path = "/api/test"; - String customMessage = "사용자 정의 오류 메시지"; - ErrorCode errorCode = ErrorCode.INVALID_INPUT; - - ErrorResponseDTO response = ErrorResponseDTO.of(errorCode, path, customMessage); - - assertThat(response.code()).isEqualTo(errorCode.getCode()); - assertThat(response.message()).isEqualTo(customMessage); - assertThat(response.path()).isEqualTo(path); - assertThat(response.status()).isEqualTo(errorCode.getStatus()); - assertThat(response.timestamp()).isNotNull(); - assertThat(response.traceId()).isNotNull(); - } - - @Test - void testErrorResponseCreationWithCustomTraceId() { - String path = "/api/test"; - String customTraceId = "custom-trace-123"; - ErrorCode errorCode = ErrorCode.INTERNAL_SERVER_ERROR; - - ErrorResponseDTO response = ErrorResponseDTO.ofWithTraceId(errorCode, path, customTraceId); - - assertThat(response.code()).isEqualTo(errorCode.getCode()); - assertThat(response.message()).isEqualTo(errorCode.getMessage()); - assertThat(response.path()).isEqualTo(path); - assertThat(response.status()).isEqualTo(errorCode.getStatus()); - assertThat(response.timestamp()).isNotNull(); - assertThat(response.traceId()).isEqualTo(customTraceId); - } - - @Test - void testErrorResponseTimestamp() { - LocalDateTime beforeCreation = LocalDateTime.now().minusSeconds(1); - - ErrorResponseDTO response = ErrorResponseDTO.of(ErrorCode.BAD_REQUEST, "/api/test"); - - LocalDateTime afterCreation = LocalDateTime.now().plusSeconds(1); - - assertThat(response.timestamp()).isBetween(beforeCreation, afterCreation); - } - - @Test - void testErrorResponseWithDifferentErrorCodes() { - String path = "/api/test"; - - ErrorResponseDTO badRequestResponse = ErrorResponseDTO.of(ErrorCode.BAD_REQUEST, path); - assertThat(badRequestResponse.status()).isEqualTo(400); - assertThat(badRequestResponse.code()).isEqualTo("E001"); - - ErrorResponseDTO unauthorizedResponse = ErrorResponseDTO.of(ErrorCode.UNAUTHORIZED, path); - assertThat(unauthorizedResponse.status()).isEqualTo(401); - assertThat(unauthorizedResponse.code()).isEqualTo("E101"); - - ErrorResponseDTO notFoundResponse = ErrorResponseDTO.of(ErrorCode.NOT_FOUND, path); - assertThat(notFoundResponse.status()).isEqualTo(404); - assertThat(notFoundResponse.code()).isEqualTo("E301"); - - ErrorResponseDTO serverErrorResponse = ErrorResponseDTO.of(ErrorCode.INTERNAL_SERVER_ERROR, path); - assertThat(serverErrorResponse.status()).isEqualTo(500); - assertThat(serverErrorResponse.code()).isEqualTo("E901"); - } - - @Test - void testErrorResponseImmutability() { - ErrorCode errorCode = ErrorCode.FORBIDDEN; - String path = "/api/forbidden"; - - ErrorResponseDTO response = ErrorResponseDTO.of(errorCode, path); - - assertThat(response.code()).isEqualTo(errorCode.getCode()); - assertThat(response.message()).isEqualTo(errorCode.getMessage()); - assertThat(response.path()).isEqualTo(path); - assertThat(response.status()).isEqualTo(errorCode.getStatus()); - } -} \ No newline at end of file + + @Test + void testErrorResponseCreationWithPath() { + String path = "/api/test"; + ErrorCode errorCode = ErrorCode.NOT_FOUND; + + ErrorResponseDTO response = ErrorResponseDTO.of(errorCode, path); + + assertThat(response.code()).isEqualTo(errorCode.getCode()); + assertThat(response.message()).isEqualTo(errorCode.getMessage()); + assertThat(response.path()).isEqualTo(path); + assertThat(response.status()).isEqualTo(errorCode.getStatus()); + assertThat(response.timestamp()).isNotNull(); + assertThat(response.traceId()).isNotNull(); + } + + @Test + void testErrorResponseCreationWithCustomMessage() { + String path = "/api/test"; + String customMessage = "사용자 정의 오류 메시지"; + ErrorCode errorCode = ErrorCode.INVALID_INPUT; + + ErrorResponseDTO response = ErrorResponseDTO.of(errorCode, path, customMessage); + + assertThat(response.code()).isEqualTo(errorCode.getCode()); + assertThat(response.message()).isEqualTo(customMessage); + assertThat(response.path()).isEqualTo(path); + assertThat(response.status()).isEqualTo(errorCode.getStatus()); + assertThat(response.timestamp()).isNotNull(); + assertThat(response.traceId()).isNotNull(); + } + + @Test + void testErrorResponseCreationWithCustomTraceId() { + String path = "/api/test"; + String customTraceId = "custom-trace-123"; + ErrorCode errorCode = ErrorCode.INTERNAL_SERVER_ERROR; + + ErrorResponseDTO response = ErrorResponseDTO.ofWithTraceId(errorCode, path, customTraceId); + + assertThat(response.code()).isEqualTo(errorCode.getCode()); + assertThat(response.message()).isEqualTo(errorCode.getMessage()); + assertThat(response.path()).isEqualTo(path); + assertThat(response.status()).isEqualTo(errorCode.getStatus()); + assertThat(response.timestamp()).isNotNull(); + assertThat(response.traceId()).isEqualTo(customTraceId); + } + + @Test + void testErrorResponseTimestamp() { + LocalDateTime beforeCreation = LocalDateTime.now().minusSeconds(1); + + ErrorResponseDTO response = ErrorResponseDTO.of(ErrorCode.BAD_REQUEST, "/api/test"); + + LocalDateTime afterCreation = LocalDateTime.now().plusSeconds(1); + + assertThat(response.timestamp()).isBetween(beforeCreation, afterCreation); + } + + @Test + void testErrorResponseWithDifferentErrorCodes() { + String path = "/api/test"; + + ErrorResponseDTO badRequestResponse = ErrorResponseDTO.of(ErrorCode.BAD_REQUEST, path); + assertThat(badRequestResponse.status()).isEqualTo(400); + assertThat(badRequestResponse.code()).isEqualTo("E001"); + + ErrorResponseDTO unauthorizedResponse = ErrorResponseDTO.of(ErrorCode.UNAUTHORIZED, path); + assertThat(unauthorizedResponse.status()).isEqualTo(401); + assertThat(unauthorizedResponse.code()).isEqualTo("E101"); + + ErrorResponseDTO notFoundResponse = ErrorResponseDTO.of(ErrorCode.NOT_FOUND, path); + assertThat(notFoundResponse.status()).isEqualTo(404); + assertThat(notFoundResponse.code()).isEqualTo("E301"); + + ErrorResponseDTO serverErrorResponse = + ErrorResponseDTO.of(ErrorCode.INTERNAL_SERVER_ERROR, path); + assertThat(serverErrorResponse.status()).isEqualTo(500); + assertThat(serverErrorResponse.code()).isEqualTo("E901"); + } + + @Test + void testErrorResponseImmutability() { + ErrorCode errorCode = ErrorCode.FORBIDDEN; + String path = "/api/forbidden"; + + ErrorResponseDTO response = ErrorResponseDTO.of(errorCode, path); + + assertThat(response.code()).isEqualTo(errorCode.getCode()); + assertThat(response.message()).isEqualTo(errorCode.getMessage()); + assertThat(response.path()).isEqualTo(path); + assertThat(response.status()).isEqualTo(errorCode.getStatus()); + } +} diff --git a/springboot/src/test/java/com/softlabs/aicontents/common/enums/ErrorCodeTest.java b/springboot/src/test/java/com/softlabs/aicontents/common/enums/ErrorCodeTest.java index 8f790fc2..ef05466b 100644 --- a/springboot/src/test/java/com/softlabs/aicontents/common/enums/ErrorCodeTest.java +++ b/springboot/src/test/java/com/softlabs/aicontents/common/enums/ErrorCodeTest.java @@ -1,71 +1,71 @@ package com.softlabs.aicontents.common.enums; +import static org.assertj.core.api.Assertions.assertThat; + import org.junit.jupiter.api.Test; import org.springframework.http.HttpStatus; -import static org.assertj.core.api.Assertions.assertThat; - class ErrorCodeTest { - - @Test - void testErrorCodeProperties() { - ErrorCode errorCode = ErrorCode.INVALID_INPUT; - - assertThat(errorCode.getHttpStatus()).isEqualTo(HttpStatus.BAD_REQUEST); - assertThat(errorCode.getCode()).isEqualTo("E002"); - assertThat(errorCode.getMessage()).isEqualTo("입력값이 올바르지 않습니다."); - assertThat(errorCode.getStatus()).isEqualTo(400); - } - - @Test - void testAllErrorCodesHaveValidProperties() { - for (ErrorCode errorCode : ErrorCode.values()) { - assertThat(errorCode.getHttpStatus()).isNotNull(); - assertThat(errorCode.getCode()).isNotEmpty(); - assertThat(errorCode.getMessage()).isNotEmpty(); - assertThat(errorCode.getStatus()).isGreaterThan(0); - } - } - - @Test - void testClientErrorCodes() { - assertThat(ErrorCode.BAD_REQUEST.getStatus()).isEqualTo(400); - assertThat(ErrorCode.UNAUTHORIZED.getStatus()).isEqualTo(401); - assertThat(ErrorCode.FORBIDDEN.getStatus()).isEqualTo(403); - assertThat(ErrorCode.NOT_FOUND.getStatus()).isEqualTo(404); - assertThat(ErrorCode.METHOD_NOT_ALLOWED.getStatus()).isEqualTo(405); - assertThat(ErrorCode.CONFLICT.getStatus()).isEqualTo(409); - } - - @Test - void testServerErrorCodes() { - assertThat(ErrorCode.INTERNAL_SERVER_ERROR.getStatus()).isEqualTo(500); - assertThat(ErrorCode.SERVICE_UNAVAILABLE.getStatus()).isEqualTo(503); - } - - @Test - void testErrorCodeUniqueness() { - ErrorCode[] errorCodes = ErrorCode.values(); - - for (int i = 0; i < errorCodes.length; i++) { - for (int j = i + 1; j < errorCodes.length; j++) { - assertThat(errorCodes[i].getCode()) - .as("Error codes should be unique: %s vs %s", errorCodes[i], errorCodes[j]) - .isNotEqualTo(errorCodes[j].getCode()); - } - } + + @Test + void testErrorCodeProperties() { + ErrorCode errorCode = ErrorCode.INVALID_INPUT; + + assertThat(errorCode.getHttpStatus()).isEqualTo(HttpStatus.BAD_REQUEST); + assertThat(errorCode.getCode()).isEqualTo("E002"); + assertThat(errorCode.getMessage()).isEqualTo("입력값이 올바르지 않습니다."); + assertThat(errorCode.getStatus()).isEqualTo(400); + } + + @Test + void testAllErrorCodesHaveValidProperties() { + for (ErrorCode errorCode : ErrorCode.values()) { + assertThat(errorCode.getHttpStatus()).isNotNull(); + assertThat(errorCode.getCode()).isNotEmpty(); + assertThat(errorCode.getMessage()).isNotEmpty(); + assertThat(errorCode.getStatus()).isGreaterThan(0); } - - @Test - void testSpecificErrorCodes() { - ErrorCode notFound = ErrorCode.NOT_FOUND; - assertThat(notFound.getCode()).isEqualTo("E301"); - assertThat(notFound.getMessage()).isEqualTo("요청한 리소스를 찾을 수 없습니다."); - assertThat(notFound.getHttpStatus()).isEqualTo(HttpStatus.NOT_FOUND); - - ErrorCode internalError = ErrorCode.INTERNAL_SERVER_ERROR; - assertThat(internalError.getCode()).isEqualTo("E901"); - assertThat(internalError.getMessage()).isEqualTo("내부 서버 오류가 발생했습니다."); - assertThat(internalError.getHttpStatus()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR); + } + + @Test + void testClientErrorCodes() { + assertThat(ErrorCode.BAD_REQUEST.getStatus()).isEqualTo(400); + assertThat(ErrorCode.UNAUTHORIZED.getStatus()).isEqualTo(401); + assertThat(ErrorCode.FORBIDDEN.getStatus()).isEqualTo(403); + assertThat(ErrorCode.NOT_FOUND.getStatus()).isEqualTo(404); + assertThat(ErrorCode.METHOD_NOT_ALLOWED.getStatus()).isEqualTo(405); + assertThat(ErrorCode.CONFLICT.getStatus()).isEqualTo(409); + } + + @Test + void testServerErrorCodes() { + assertThat(ErrorCode.INTERNAL_SERVER_ERROR.getStatus()).isEqualTo(500); + assertThat(ErrorCode.SERVICE_UNAVAILABLE.getStatus()).isEqualTo(503); + } + + @Test + void testErrorCodeUniqueness() { + ErrorCode[] errorCodes = ErrorCode.values(); + + for (int i = 0; i < errorCodes.length; i++) { + for (int j = i + 1; j < errorCodes.length; j++) { + assertThat(errorCodes[i].getCode()) + .as("Error codes should be unique: %s vs %s", errorCodes[i], errorCodes[j]) + .isNotEqualTo(errorCodes[j].getCode()); + } } -} \ No newline at end of file + } + + @Test + void testSpecificErrorCodes() { + ErrorCode notFound = ErrorCode.NOT_FOUND; + assertThat(notFound.getCode()).isEqualTo("E301"); + assertThat(notFound.getMessage()).isEqualTo("요청한 리소스를 찾을 수 없습니다."); + assertThat(notFound.getHttpStatus()).isEqualTo(HttpStatus.NOT_FOUND); + + ErrorCode internalError = ErrorCode.INTERNAL_SERVER_ERROR; + assertThat(internalError.getCode()).isEqualTo("E901"); + assertThat(internalError.getMessage()).isEqualTo("내부 서버 오류가 발생했습니다."); + assertThat(internalError.getHttpStatus()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR); + } +} diff --git a/springboot/src/test/java/com/softlabs/aicontents/common/exception/BusinessExceptionTest.java b/springboot/src/test/java/com/softlabs/aicontents/common/exception/BusinessExceptionTest.java index 7ce74f9c..91101994 100644 --- a/springboot/src/test/java/com/softlabs/aicontents/common/exception/BusinessExceptionTest.java +++ b/springboot/src/test/java/com/softlabs/aicontents/common/exception/BusinessExceptionTest.java @@ -1,125 +1,125 @@ package com.softlabs.aicontents.common.exception; +import static org.assertj.core.api.Assertions.assertThat; + import com.softlabs.aicontents.common.enums.ErrorCode; import org.junit.jupiter.api.Test; -import static org.assertj.core.api.Assertions.assertThat; - class BusinessExceptionTest { - - @Test - void testBusinessExceptionWithErrorCode() { - ErrorCode errorCode = ErrorCode.NOT_FOUND; - - BusinessException exception = new BusinessException(errorCode); - - assertThat(exception.getErrorCode()).isEqualTo(errorCode); - assertThat(exception.getMessage()).isEqualTo(errorCode.getMessage()); - assertThat(exception.getCustomMessage()).isNull(); - assertThat(exception.getEffectiveMessage()).isEqualTo(errorCode.getMessage()); - } - - @Test - void testBusinessExceptionWithCustomMessage() { - ErrorCode errorCode = ErrorCode.INVALID_INPUT; - String customMessage = "사용자 정의 오류 메시지"; - - BusinessException exception = new BusinessException(errorCode, customMessage); - - assertThat(exception.getErrorCode()).isEqualTo(errorCode); - assertThat(exception.getMessage()).isEqualTo(customMessage); - assertThat(exception.getCustomMessage()).isEqualTo(customMessage); - assertThat(exception.getEffectiveMessage()).isEqualTo(customMessage); - } - - @Test - void testBusinessExceptionWithCause() { - ErrorCode errorCode = ErrorCode.INTERNAL_SERVER_ERROR; - Throwable cause = new RuntimeException("원인 예외"); - - BusinessException exception = new BusinessException(errorCode, cause); - - assertThat(exception.getErrorCode()).isEqualTo(errorCode); - assertThat(exception.getMessage()).isEqualTo(errorCode.getMessage()); - assertThat(exception.getCause()).isEqualTo(cause); - assertThat(exception.getCustomMessage()).isNull(); - assertThat(exception.getEffectiveMessage()).isEqualTo(errorCode.getMessage()); - } - - @Test - void testBusinessExceptionWithCustomMessageAndCause() { - ErrorCode errorCode = ErrorCode.DATABASE_ERROR; - String customMessage = "데이터베이스 연결 실패"; - Throwable cause = new RuntimeException("Connection timeout"); - - BusinessException exception = new BusinessException(errorCode, customMessage, cause); - - assertThat(exception.getErrorCode()).isEqualTo(errorCode); - assertThat(exception.getMessage()).isEqualTo(customMessage); - assertThat(exception.getCause()).isEqualTo(cause); - assertThat(exception.getCustomMessage()).isEqualTo(customMessage); - assertThat(exception.getEffectiveMessage()).isEqualTo(customMessage); - } - - @Test - void testEffectiveMessageWithoutCustomMessage() { - ErrorCode errorCode = ErrorCode.FORBIDDEN; - BusinessException exception = new BusinessException(errorCode); - - assertThat(exception.getEffectiveMessage()).isEqualTo(errorCode.getMessage()); - } - - @Test - void testEffectiveMessageWithCustomMessage() { - ErrorCode errorCode = ErrorCode.FORBIDDEN; - String customMessage = "특정 리소스에 대한 접근 권한이 없습니다"; - BusinessException exception = new BusinessException(errorCode, customMessage); - - assertThat(exception.getEffectiveMessage()).isEqualTo(customMessage); - } - - @Test - void testBusinessExceptionInheritance() { - ErrorCode errorCode = ErrorCode.BAD_REQUEST; - BusinessException exception = new BusinessException(errorCode); - - assertThat(exception).isInstanceOf(RuntimeException.class); - assertThat(exception).isInstanceOf(Exception.class); - assertThat(exception).isInstanceOf(Throwable.class); - } - - @Test - void testBusinessExceptionWithDifferentErrorCodes() { - BusinessException badRequestException = new BusinessException(ErrorCode.BAD_REQUEST); - assertThat(badRequestException.getErrorCode()).isEqualTo(ErrorCode.BAD_REQUEST); - - BusinessException unauthorizedException = new BusinessException(ErrorCode.UNAUTHORIZED); - assertThat(unauthorizedException.getErrorCode()).isEqualTo(ErrorCode.UNAUTHORIZED); - - BusinessException notFoundException = new BusinessException(ErrorCode.NOT_FOUND); - assertThat(notFoundException.getErrorCode()).isEqualTo(ErrorCode.NOT_FOUND); - - BusinessException serverErrorException = new BusinessException(ErrorCode.INTERNAL_SERVER_ERROR); - assertThat(serverErrorException.getErrorCode()).isEqualTo(ErrorCode.INTERNAL_SERVER_ERROR); - } - - @Test - void testBusinessExceptionMessageConsistency() { - ErrorCode errorCode = ErrorCode.CONFLICT; - BusinessException exception = new BusinessException(errorCode); - - assertThat(exception.getMessage()).isEqualTo(errorCode.getMessage()); - assertThat(exception.getEffectiveMessage()).isEqualTo(errorCode.getMessage()); - } - - @Test - void testCustomMessageOverridesDefaultMessage() { - ErrorCode errorCode = ErrorCode.SERVICE_UNAVAILABLE; - String customMessage = "현재 시스템 점검 중입니다"; - BusinessException exception = new BusinessException(errorCode, customMessage); - - assertThat(exception.getMessage()).isEqualTo(customMessage); - assertThat(exception.getEffectiveMessage()).isEqualTo(customMessage); - assertThat(exception.getMessage()).isNotEqualTo(errorCode.getMessage()); - } -} \ No newline at end of file + + @Test + void testBusinessExceptionWithErrorCode() { + ErrorCode errorCode = ErrorCode.NOT_FOUND; + + BusinessException exception = new BusinessException(errorCode); + + assertThat(exception.getErrorCode()).isEqualTo(errorCode); + assertThat(exception.getMessage()).isEqualTo(errorCode.getMessage()); + assertThat(exception.getCustomMessage()).isNull(); + assertThat(exception.getEffectiveMessage()).isEqualTo(errorCode.getMessage()); + } + + @Test + void testBusinessExceptionWithCustomMessage() { + ErrorCode errorCode = ErrorCode.INVALID_INPUT; + String customMessage = "사용자 정의 오류 메시지"; + + BusinessException exception = new BusinessException(errorCode, customMessage); + + assertThat(exception.getErrorCode()).isEqualTo(errorCode); + assertThat(exception.getMessage()).isEqualTo(customMessage); + assertThat(exception.getCustomMessage()).isEqualTo(customMessage); + assertThat(exception.getEffectiveMessage()).isEqualTo(customMessage); + } + + @Test + void testBusinessExceptionWithCause() { + ErrorCode errorCode = ErrorCode.INTERNAL_SERVER_ERROR; + Throwable cause = new RuntimeException("원인 예외"); + + BusinessException exception = new BusinessException(errorCode, cause); + + assertThat(exception.getErrorCode()).isEqualTo(errorCode); + assertThat(exception.getMessage()).isEqualTo(errorCode.getMessage()); + assertThat(exception.getCause()).isEqualTo(cause); + assertThat(exception.getCustomMessage()).isNull(); + assertThat(exception.getEffectiveMessage()).isEqualTo(errorCode.getMessage()); + } + + @Test + void testBusinessExceptionWithCustomMessageAndCause() { + ErrorCode errorCode = ErrorCode.DATABASE_ERROR; + String customMessage = "데이터베이스 연결 실패"; + Throwable cause = new RuntimeException("Connection timeout"); + + BusinessException exception = new BusinessException(errorCode, customMessage, cause); + + assertThat(exception.getErrorCode()).isEqualTo(errorCode); + assertThat(exception.getMessage()).isEqualTo(customMessage); + assertThat(exception.getCause()).isEqualTo(cause); + assertThat(exception.getCustomMessage()).isEqualTo(customMessage); + assertThat(exception.getEffectiveMessage()).isEqualTo(customMessage); + } + + @Test + void testEffectiveMessageWithoutCustomMessage() { + ErrorCode errorCode = ErrorCode.FORBIDDEN; + BusinessException exception = new BusinessException(errorCode); + + assertThat(exception.getEffectiveMessage()).isEqualTo(errorCode.getMessage()); + } + + @Test + void testEffectiveMessageWithCustomMessage() { + ErrorCode errorCode = ErrorCode.FORBIDDEN; + String customMessage = "특정 리소스에 대한 접근 권한이 없습니다"; + BusinessException exception = new BusinessException(errorCode, customMessage); + + assertThat(exception.getEffectiveMessage()).isEqualTo(customMessage); + } + + @Test + void testBusinessExceptionInheritance() { + ErrorCode errorCode = ErrorCode.BAD_REQUEST; + BusinessException exception = new BusinessException(errorCode); + + assertThat(exception).isInstanceOf(RuntimeException.class); + assertThat(exception).isInstanceOf(Exception.class); + assertThat(exception).isInstanceOf(Throwable.class); + } + + @Test + void testBusinessExceptionWithDifferentErrorCodes() { + BusinessException badRequestException = new BusinessException(ErrorCode.BAD_REQUEST); + assertThat(badRequestException.getErrorCode()).isEqualTo(ErrorCode.BAD_REQUEST); + + BusinessException unauthorizedException = new BusinessException(ErrorCode.UNAUTHORIZED); + assertThat(unauthorizedException.getErrorCode()).isEqualTo(ErrorCode.UNAUTHORIZED); + + BusinessException notFoundException = new BusinessException(ErrorCode.NOT_FOUND); + assertThat(notFoundException.getErrorCode()).isEqualTo(ErrorCode.NOT_FOUND); + + BusinessException serverErrorException = new BusinessException(ErrorCode.INTERNAL_SERVER_ERROR); + assertThat(serverErrorException.getErrorCode()).isEqualTo(ErrorCode.INTERNAL_SERVER_ERROR); + } + + @Test + void testBusinessExceptionMessageConsistency() { + ErrorCode errorCode = ErrorCode.CONFLICT; + BusinessException exception = new BusinessException(errorCode); + + assertThat(exception.getMessage()).isEqualTo(errorCode.getMessage()); + assertThat(exception.getEffectiveMessage()).isEqualTo(errorCode.getMessage()); + } + + @Test + void testCustomMessageOverridesDefaultMessage() { + ErrorCode errorCode = ErrorCode.SERVICE_UNAVAILABLE; + String customMessage = "현재 시스템 점검 중입니다"; + BusinessException exception = new BusinessException(errorCode, customMessage); + + assertThat(exception.getMessage()).isEqualTo(customMessage); + assertThat(exception.getEffectiveMessage()).isEqualTo(customMessage); + assertThat(exception.getMessage()).isNotEqualTo(errorCode.getMessage()); + } +} From 1c04fb2b2230bd9e5852c66bba88ccb91181dfa9 Mon Sep 17 00:00:00 2001 From: "skykim5538@gmail.com" Date: Sun, 14 Sep 2025 19:57:46 +0900 Subject: [PATCH 19/20] =?UTF-8?q?feat:=20=EC=8A=A4=EC=BC=80=EC=A4=84?= =?UTF-8?q?=EB=9F=AC=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 스케줄 생성 API 구현 - 요청/응답 DTO,VO 객체 생성 --- .../dto/request/ScheduleTasksRequestDTO.java | 46 +++++++++++ .../common/dto/response/ApiResponseDTO.java | 7 +- .../response/ScheduleTasksResponseDTO.java | 10 +++ .../controller/ScheduleEngineController.java | 63 +++++---------- .../scheduler/dto/PipeResultDataDTO.java | 4 - .../dto/pipeLineDTO/PublishingStatus.java | 10 --- .../mapper/ScheduleEngineMapper.java | 13 ++++ .../service/ScheduleEngineService.java | 76 +++++++++++++++++++ .../domain/scheduler/vo/PipelineStatusVO.java | 10 --- .../vo/request/SchedulerRequestVO.java | 37 +++++++++ .../vo/response/ScheduleResponseVO.java | 11 +++ .../domain/scheduler/ScheduleEngineMapper.xml | 37 +++++++++ .../mappers/scheduler/ScheduledTaskMapper.xml | 33 -------- 13 files changed, 256 insertions(+), 101 deletions(-) create mode 100644 springboot/src/main/java/com/softlabs/aicontents/common/dto/request/ScheduleTasksRequestDTO.java create mode 100644 springboot/src/main/java/com/softlabs/aicontents/common/dto/response/ScheduleTasksResponseDTO.java delete mode 100644 springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/pipeLineDTO/PublishingStatus.java create mode 100644 springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/mapper/ScheduleEngineMapper.java create mode 100644 springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/ScheduleEngineService.java delete mode 100644 springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/vo/PipelineStatusVO.java create mode 100644 springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/vo/request/SchedulerRequestVO.java create mode 100644 springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/vo/response/ScheduleResponseVO.java create mode 100644 springboot/src/main/resources/mappers/domain/scheduler/ScheduleEngineMapper.xml delete mode 100644 springboot/src/main/resources/mappers/scheduler/ScheduledTaskMapper.xml diff --git a/springboot/src/main/java/com/softlabs/aicontents/common/dto/request/ScheduleTasksRequestDTO.java b/springboot/src/main/java/com/softlabs/aicontents/common/dto/request/ScheduleTasksRequestDTO.java new file mode 100644 index 00000000..49387b77 --- /dev/null +++ b/springboot/src/main/java/com/softlabs/aicontents/common/dto/request/ScheduleTasksRequestDTO.java @@ -0,0 +1,46 @@ +package com.softlabs.aicontents.common.dto.request; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + + +// 공통 응답 DTO +// FE -> BE(DTO) 대시보드 중 "스케줄 관리" 카드 +// 단순 객체 전달용 +// 용도 및 의미만 맞추고 XML에는 alias 적용 + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Schema(description = "ScheduleTasksRequestDTO - 스케줄 작업 요청 객체") +public class ScheduleTasksRequestDTO { + + + @Schema(description = "스케줄러 명칭", example = "Untitled Schedule", requiredMode = Schema.RequiredMode.REQUIRED) + private String taskName; + + @Schema(description = "크론 표현식", example = "0 8 * * *") + private String cronExpression ; + + @Schema(description = "실행 주기", example="08:00") + private String scheduleType ; // "매일 실행/ 주간 실행/ 월간 실행" + + @Schema(description ="실행 시간", example="08:00") + private String executionTime ; // "HH:MM" 자동 실행 시간 + + @Schema(description ="키워드 추출 개수" , example="50") + private int keywordCount ; // 추출 키워드 수량 + + @Schema(description ="블로그 발행 개수", example="1") + private int contentCount ; // 블로그 발행 수량 + + @Schema(description ="AI 모델명", example="OpenAI GPT-4") + private String aiModel ; // AI 모델명 (예: "OpenAI GPT-4") + + +} + diff --git a/springboot/src/main/java/com/softlabs/aicontents/common/dto/response/ApiResponseDTO.java b/springboot/src/main/java/com/softlabs/aicontents/common/dto/response/ApiResponseDTO.java index bb11d074..08ff7703 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/common/dto/response/ApiResponseDTO.java +++ b/springboot/src/main/java/com/softlabs/aicontents/common/dto/response/ApiResponseDTO.java @@ -1,7 +1,12 @@ package com.softlabs.aicontents.common.dto.response; // 공통 응답 DTO -public record ApiResponseDTO(boolean success, T data, String message) { +public record ApiResponseDTO( + boolean success, + T data, + String message) + +{ // 성공 응답 생성 public static ApiResponseDTO success(T data) { return new ApiResponseDTO<>(true, data, null); diff --git a/springboot/src/main/java/com/softlabs/aicontents/common/dto/response/ScheduleTasksResponseDTO.java b/springboot/src/main/java/com/softlabs/aicontents/common/dto/response/ScheduleTasksResponseDTO.java new file mode 100644 index 00000000..1596b5e4 --- /dev/null +++ b/springboot/src/main/java/com/softlabs/aicontents/common/dto/response/ScheduleTasksResponseDTO.java @@ -0,0 +1,10 @@ +package com.softlabs.aicontents.common.dto.response; + +import lombok.Data; + +@Data +public class ScheduleTasksResponseDTO { + + private int taskId; + +} diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/controller/ScheduleEngineController.java b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/controller/ScheduleEngineController.java index eb083958..2c60ef81 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/controller/ScheduleEngineController.java +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/controller/ScheduleEngineController.java @@ -1,23 +1,22 @@ package com.softlabs.aicontents.domain.scheduler.controller; +import com.softlabs.aicontents.common.dto.request.ScheduleTasksRequestDTO; import com.softlabs.aicontents.common.dto.response.ApiResponseDTO; +import com.softlabs.aicontents.common.dto.response.ScheduleTasksResponseDTO; import com.softlabs.aicontents.domain.orchestration.PipelineService; -import com.softlabs.aicontents.domain.scheduler.dto.PipeResultDataDTO; +import com.softlabs.aicontents.domain.scheduler.service.ScheduleEngineService; +import io.swagger.v3.oas.annotations.Operation; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.EnableScheduling; -import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; @Component @Slf4j @EnableScheduling @RestController -@RequestMapping("/v1/pipeline") +@RequestMapping("/v1") public class ScheduleEngineController { private int executionCount = 0; @@ -25,50 +24,28 @@ public class ScheduleEngineController { private boolean isCompleted = false; @Autowired - private PipelineService pipelineService; - - // 초 분 시 일 월 요일 - // @Scheduled(cron = "0 58 21 * * *") + private PipelineService pipelineService; //파이프라인(=오케스트레이션) + @Autowired + private ScheduleEngineService scheduleEngineService; //스케줄 엔진 - @Scheduled(cron = "1/30 * * * * *") - /// 12시 18분 정적 실행 - /// todo : 1. 파이프라인 3회 실행 후 종료(for문) - /// todo : 2. 파이프라인 3회 재시도 /예외처리 - /// 10. 파이프라인 실행 -// @PostMapping("/execute") -// public void executePipline() { -// /// 파이프라인 실행 메서드 호출 -// pipelineService.executionPipline(); -// } + /// 08. 스케줄 생성 + @Operation(summary = "스케줄 생성 API",description = "생성할 스케줄의 상세 정보입니다.") + @PostMapping("/schedule") + public ApiResponseDTO setSchedule(ScheduleTasksRequestDTO scheduleTasksRequestDTO) { - /// 11. 파이프라인 상태 조회 - @GetMapping("/status/{executionId}") - public ApiResponseDTO checkStatus() { + // 확인 메세지 + System.out.println("scheduleTasksRequestDTO를 전달받음.=>" + scheduleTasksRequestDTO.toString()); try { + ScheduleTasksResponseDTO scheduleTasksResponseDTO = scheduleEngineService.scheduleEngine(scheduleTasksRequestDTO); - PipeResultDataDTO pipeResultDataDTO = pipelineService.executionPipline(); - String successMesg = "파이프라인 상태 데이터를 pipeResultDataDTO에 저장 완료"; - - return ApiResponseDTO.success(pipeResultDataDTO, successMesg); + return ApiResponseDTO.success(scheduleTasksResponseDTO,"새로운 스케줄 저장 완료"); + } catch (Exception e) { - }catch (Exception e){ - return ApiResponseDTO.error("파이프라인 상태 조회 실패"); + return ApiResponseDTO.error("스케줄 저장 실패"+e.getMessage()); } - /// todo : 상태 조회 로직 - /// 파이프 라인이 종료되면, 각 기능들을 지나오면서 - //DB에서 조회한 상태, 키워드, 등등이 VO로 저장되어 있을 것이고 - //이것은 인터페이스에 저장되게 할 것이다. - //그래서 하나의 파이프라인이 끝나면, 이 VO들이 저장된 상태가 되게 한다. - // VO 는 대시보드에서 요청하는 DTO - //결과적으로 VO를 DTO로 보내줘야 함. - } - - /// 12. 파이프라인 제어 - @PostMapping("/control/{executionId}") - public void controlPipeline() { - /// todo : 파이프라인 제어 로직 } } + diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/PipeResultDataDTO.java b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/PipeResultDataDTO.java index ec338f18..19efea4f 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/PipeResultDataDTO.java +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/PipeResultDataDTO.java @@ -8,10 +8,8 @@ import java.util.List; -//최상위 응답 @Data @Schema - public class PipeResultDataDTO { // 실행정보 @@ -21,7 +19,6 @@ public class PipeResultDataDTO { String completedAt; String currentStage; - // 각 단계별 진행 상황 ProgressResult progressResult; @@ -31,6 +28,5 @@ public class PipeResultDataDTO { // 로그 정보 List logs; - } diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/pipeLineDTO/PublishingStatus.java b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/pipeLineDTO/PublishingStatus.java deleted file mode 100644 index c7f96286..00000000 --- a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/dto/pipeLineDTO/PublishingStatus.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.softlabs.aicontents.domain.scheduler.dto.pipeLineDTO; - -import lombok.Data; - -@Data -public class PublishingStatus { - String platform; - String status; - String url; -} diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/mapper/ScheduleEngineMapper.java b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/mapper/ScheduleEngineMapper.java new file mode 100644 index 00000000..310497c9 --- /dev/null +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/mapper/ScheduleEngineMapper.java @@ -0,0 +1,13 @@ +package com.softlabs.aicontents.domain.scheduler.mapper; + +import com.softlabs.aicontents.domain.scheduler.vo.request.SchedulerRequestVO; +import com.softlabs.aicontents.domain.scheduler.vo.response.ScheduleResponseVO; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface ScheduleEngineMapper { + + int insertSchedule( SchedulerRequestVO schedulerRequestVO); + + ScheduleResponseVO selectScheduleEngines(); +} diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/ScheduleEngineService.java b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/ScheduleEngineService.java new file mode 100644 index 00000000..22633911 --- /dev/null +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/ScheduleEngineService.java @@ -0,0 +1,76 @@ +package com.softlabs.aicontents.domain.scheduler.service; + +import com.softlabs.aicontents.common.dto.request.ScheduleTasksRequestDTO; +import com.softlabs.aicontents.common.dto.response.ScheduleTasksResponseDTO; +import com.softlabs.aicontents.domain.scheduler.mapper.ScheduleEngineMapper; +import com.softlabs.aicontents.domain.scheduler.vo.request.SchedulerRequestVO; +import com.softlabs.aicontents.domain.scheduler.vo.response.ScheduleResponseVO; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + + +@Service +@Slf4j +public class ScheduleEngineService { + + @Autowired + private ScheduleEngineMapper scheduleEngineMapper; + + @Transactional + public ScheduleTasksResponseDTO scheduleEngine(ScheduleTasksRequestDTO scheduleTasksRequestDTO) { + + try { + // 스케줄 생성 및 taskId 반환 + int insertResult = createSchedule(scheduleTasksRequestDTO); + int taskId = selectSchedule().getTaskId(); + ScheduleTasksResponseDTO resDTO = new ScheduleTasksResponseDTO(); + resDTO.setTaskId(taskId); + + return resDTO; + + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + + // DTO -> VO로 변환 + private SchedulerRequestVO convertDTOtoVO(ScheduleTasksRequestDTO scheduleTasksRequestDTO) { + + SchedulerRequestVO schedulerRequestVO = new SchedulerRequestVO(); + + schedulerRequestVO.setTaskName(scheduleTasksRequestDTO.getTaskName()); + schedulerRequestVO.setCronExpression(scheduleTasksRequestDTO.getCronExpression()); + schedulerRequestVO.setScheduleType(scheduleTasksRequestDTO.getScheduleType()); + schedulerRequestVO.setExecutionTime(scheduleTasksRequestDTO.getExecutionTime()); + schedulerRequestVO.setKeywordCount(scheduleTasksRequestDTO.getKeywordCount()); + schedulerRequestVO.setContentCount(scheduleTasksRequestDTO.getContentCount()); + schedulerRequestVO.setAiModel(scheduleTasksRequestDTO.getAiModel()); + + return schedulerRequestVO; + } + + + // DB 저장 로직 + public int createSchedule(ScheduleTasksRequestDTO scheduleTasksRequestDTO) { + + SchedulerRequestVO schedulerRequestVO = this.convertDTOtoVO(scheduleTasksRequestDTO); + + int resultInsert = scheduleEngineMapper.insertSchedule(schedulerRequestVO); + log.info("DB 저장 메퍼 실행 완료"); + + + return resultInsert; + } + + + public ScheduleResponseVO selectSchedule() { + + ScheduleResponseVO resultSelect = scheduleEngineMapper.selectScheduleEngines(); + + return resultSelect; + } + +} \ No newline at end of file diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/vo/PipelineStatusVO.java b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/vo/PipelineStatusVO.java deleted file mode 100644 index 1b2ba173..00000000 --- a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/vo/PipelineStatusVO.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.softlabs.aicontents.domain.scheduler.vo; - -public record PipelineStatusVO( - - /// === 성공 시 === - - - -) { -} diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/vo/request/SchedulerRequestVO.java b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/vo/request/SchedulerRequestVO.java new file mode 100644 index 00000000..6875bbef --- /dev/null +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/vo/request/SchedulerRequestVO.java @@ -0,0 +1,37 @@ +package com.softlabs.aicontents.domain.scheduler.vo.request; + +import lombok.Data; + +//서비스 -> DB로 보내는 객체 보관용 클래스 +// 하나의 VO가 모든 데이터를 관리 + +@Data +public class SchedulerRequestVO { + + /// 스케줄 관련 데이터 + private String scheduleType; // "매일(A)/ 주간 실행(B)/ 월간 실행(C)" + private String executionTime; // "HH:MM" 자동 실행 시간 + private int keywordCount; // 추출 키워드 수량 + private int contentCount; // 블로그 발행 수량 + private String aiModel; // AI 모델명 (예: "OpenAI GPT-4") + + + /// 파이프라인 관련 데이터 + + int taskId; + String taskName; + String taskDescription; + String cronExpression; + String taskType; + boolean isActive; + int maxRetryCount; + int timeoutMinutes; + String nextExecution; + String lastExecution; + String pipelineConfig; + String createdBy; + String updatedBy; + String createdAt; + String updatedAt; + +} diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/vo/response/ScheduleResponseVO.java b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/vo/response/ScheduleResponseVO.java new file mode 100644 index 00000000..d53f61f9 --- /dev/null +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/vo/response/ScheduleResponseVO.java @@ -0,0 +1,11 @@ +package com.softlabs.aicontents.domain.scheduler.vo.response; + + +import lombok.Data; + +@Data +public class ScheduleResponseVO { + + // TASK 식별자 + public int taskId; +} diff --git a/springboot/src/main/resources/mappers/domain/scheduler/ScheduleEngineMapper.xml b/springboot/src/main/resources/mappers/domain/scheduler/ScheduleEngineMapper.xml new file mode 100644 index 00000000..c90dd4f3 --- /dev/null +++ b/springboot/src/main/resources/mappers/domain/scheduler/ScheduleEngineMapper.xml @@ -0,0 +1,37 @@ + + + + + + + + + +/* ScheduleEngineMapper.insertSchedule*/ + INSERT INTO SCHEDULED_TASKS ( TASK_ID, + TASK_NAME, + CRON_EXPRESSION, + SCHEDULE_TYPE, + EXECUTION_TIME, + KEYWORD_COUNT, + CONTENT_COUNT, + AI_MODEL + ) + VALUES ( SCHEDULE_TASK_SEQUENCES.NEXTVAL, + #{taskName}, + #{cronExpression}, + #{scheduleType}, + #{executionTime}, + #{keywordCount}, + #{contentCount}, + #{aiModel} + ) + + + diff --git a/springboot/src/main/resources/mappers/scheduler/ScheduledTaskMapper.xml b/springboot/src/main/resources/mappers/scheduler/ScheduledTaskMapper.xml deleted file mode 100644 index 66e6ec45..00000000 --- a/springboot/src/main/resources/mappers/scheduler/ScheduledTaskMapper.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 3fcfaaa867e4600b0197852939e95dff149a4c5d Mon Sep 17 00:00:00 2001 From: sharon Date: Sun, 14 Sep 2025 21:26:52 +0900 Subject: [PATCH 20/20] =?UTF-8?q?feat:=20=EC=8A=A4=EC=BC=80=EC=A4=84?= =?UTF-8?q?=EC=97=94=EC=A7=84BE-FE=20=ED=86=B5=ED=95=A9=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/dto/request/ScheduleTasksRequestDTO.java | 4 ++-- .../controller/ScheduleEngineController.java | 2 +- .../scheduler/service/ScheduleEngineService.java | 13 +++++++------ .../scheduler/vo/request/SchedulerRequestVO.java | 4 ++-- .../domain/scheduler/ScheduleEngineMapper.xml | 8 ++------ 5 files changed, 14 insertions(+), 17 deletions(-) diff --git a/springboot/src/main/java/com/softlabs/aicontents/common/dto/request/ScheduleTasksRequestDTO.java b/springboot/src/main/java/com/softlabs/aicontents/common/dto/request/ScheduleTasksRequestDTO.java index 49387b77..62e8ec8f 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/common/dto/request/ScheduleTasksRequestDTO.java +++ b/springboot/src/main/java/com/softlabs/aicontents/common/dto/request/ScheduleTasksRequestDTO.java @@ -27,7 +27,7 @@ public class ScheduleTasksRequestDTO { private String cronExpression ; @Schema(description = "실행 주기", example="08:00") - private String scheduleType ; // "매일 실행/ 주간 실행/ 월간 실행" + private String executionCycle; // "매일 실행/ 주간 실행/ 월간 실행" @Schema(description ="실행 시간", example="08:00") private String executionTime ; // "HH:MM" 자동 실행 시간 @@ -36,7 +36,7 @@ public class ScheduleTasksRequestDTO { private int keywordCount ; // 추출 키워드 수량 @Schema(description ="블로그 발행 개수", example="1") - private int contentCount ; // 블로그 발행 수량 + private int publishCount; // 블로그 발행 수량 @Schema(description ="AI 모델명", example="OpenAI GPT-4") private String aiModel ; // AI 모델명 (예: "OpenAI GPT-4") diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/controller/ScheduleEngineController.java b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/controller/ScheduleEngineController.java index 2c60ef81..071ff99e 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/controller/ScheduleEngineController.java +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/controller/ScheduleEngineController.java @@ -33,7 +33,7 @@ public class ScheduleEngineController { /// 08. 스케줄 생성 @Operation(summary = "스케줄 생성 API",description = "생성할 스케줄의 상세 정보입니다.") @PostMapping("/schedule") - public ApiResponseDTO setSchedule(ScheduleTasksRequestDTO scheduleTasksRequestDTO) { + public ApiResponseDTO setSchedule(@RequestBody ScheduleTasksRequestDTO scheduleTasksRequestDTO) { // 확인 메세지 System.out.println("scheduleTasksRequestDTO를 전달받음.=>" + scheduleTasksRequestDTO.toString()); diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/ScheduleEngineService.java b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/ScheduleEngineService.java index 22633911..85c63717 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/ScheduleEngineService.java +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/service/ScheduleEngineService.java @@ -41,24 +41,24 @@ private SchedulerRequestVO convertDTOtoVO(ScheduleTasksRequestDTO scheduleTasksR SchedulerRequestVO schedulerRequestVO = new SchedulerRequestVO(); - schedulerRequestVO.setTaskName(scheduleTasksRequestDTO.getTaskName()); - schedulerRequestVO.setCronExpression(scheduleTasksRequestDTO.getCronExpression()); - schedulerRequestVO.setScheduleType(scheduleTasksRequestDTO.getScheduleType()); +// schedulerRequestVO.setTaskName(scheduleTasksRequestDTO.getTaskName()); +// schedulerRequestVO.setCronExpression(scheduleTasksRequestDTO.getCronExpression()); + schedulerRequestVO.setExecutionCycle(scheduleTasksRequestDTO.getExecutionCycle()); schedulerRequestVO.setExecutionTime(scheduleTasksRequestDTO.getExecutionTime()); schedulerRequestVO.setKeywordCount(scheduleTasksRequestDTO.getKeywordCount()); - schedulerRequestVO.setContentCount(scheduleTasksRequestDTO.getContentCount()); + schedulerRequestVO.setPublishCount(scheduleTasksRequestDTO.getPublishCount()); schedulerRequestVO.setAiModel(scheduleTasksRequestDTO.getAiModel()); return schedulerRequestVO; } - - // DB 저장 로직 + // 스케줄 생성 public int createSchedule(ScheduleTasksRequestDTO scheduleTasksRequestDTO) { SchedulerRequestVO schedulerRequestVO = this.convertDTOtoVO(scheduleTasksRequestDTO); int resultInsert = scheduleEngineMapper.insertSchedule(schedulerRequestVO); + log.info("DB 저장 메퍼 실행 완료"); @@ -66,6 +66,7 @@ public int createSchedule(ScheduleTasksRequestDTO scheduleTasksRequestDTO) { } + // 스케줄 조회 public ScheduleResponseVO selectSchedule() { ScheduleResponseVO resultSelect = scheduleEngineMapper.selectScheduleEngines(); diff --git a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/vo/request/SchedulerRequestVO.java b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/vo/request/SchedulerRequestVO.java index 6875bbef..af2ffb31 100644 --- a/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/vo/request/SchedulerRequestVO.java +++ b/springboot/src/main/java/com/softlabs/aicontents/domain/scheduler/vo/request/SchedulerRequestVO.java @@ -9,10 +9,10 @@ public class SchedulerRequestVO { /// 스케줄 관련 데이터 - private String scheduleType; // "매일(A)/ 주간 실행(B)/ 월간 실행(C)" + private String executionCycle; // "매일(A)/ 주간 실행(B)/ 월간 실행(C)" private String executionTime; // "HH:MM" 자동 실행 시간 private int keywordCount; // 추출 키워드 수량 - private int contentCount; // 블로그 발행 수량 + private int publishCount; // 블로그 발행 수량 private String aiModel; // AI 모델명 (예: "OpenAI GPT-4") diff --git a/springboot/src/main/resources/mappers/domain/scheduler/ScheduleEngineMapper.xml b/springboot/src/main/resources/mappers/domain/scheduler/ScheduleEngineMapper.xml index c90dd4f3..5fe7bc75 100644 --- a/springboot/src/main/resources/mappers/domain/scheduler/ScheduleEngineMapper.xml +++ b/springboot/src/main/resources/mappers/domain/scheduler/ScheduleEngineMapper.xml @@ -15,8 +15,6 @@ /* ScheduleEngineMapper.insertSchedule*/ INSERT INTO SCHEDULED_TASKS ( TASK_ID, - TASK_NAME, - CRON_EXPRESSION, SCHEDULE_TYPE, EXECUTION_TIME, KEYWORD_COUNT, @@ -24,12 +22,10 @@ AI_MODEL ) VALUES ( SCHEDULE_TASK_SEQUENCES.NEXTVAL, - #{taskName}, - #{cronExpression}, - #{scheduleType}, + #{executionCycle}, #{executionTime}, #{keywordCount}, - #{contentCount}, + #{publishCount}, #{aiModel} )