diff --git a/apps/pre-processing-service/poetry.lock b/apps/pre-processing-service/poetry.lock index 16e99d6b..ebfc906b 100644 --- a/apps/pre-processing-service/poetry.lock +++ b/apps/pre-processing-service/poetry.lock @@ -323,18 +323,18 @@ uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "boto3" -version = "1.40.39" +version = "1.40.40" description = "The AWS SDK for Python" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "boto3-1.40.39-py3-none-any.whl", hash = "sha256:e2cab5606269fe9f428981892aa592b7e0c087a038774475fa4cd6c8b5fe0a99"}, - {file = "boto3-1.40.39.tar.gz", hash = "sha256:27ca06d4d6f838b056b4935c9eceb92c8d125dbe0e895c5583bcf7130627dcd2"}, + {file = "boto3-1.40.40-py3-none-any.whl", hash = "sha256:385904de68623e1c341bdc095d94a30006843032c912adeb1e0752a343632ec6"}, + {file = "boto3-1.40.40.tar.gz", hash = "sha256:f384d3a0410d0f1a4d4ae7aa69c41d0549c6ca5a76667dc25fc97d50ad6db740"}, ] [package.dependencies] -botocore = ">=1.40.39,<1.41.0" +botocore = ">=1.40.40,<1.41.0" jmespath = ">=0.7.1,<2.0.0" s3transfer = ">=0.14.0,<0.15.0" @@ -343,14 +343,14 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] [[package]] name = "botocore" -version = "1.40.39" +version = "1.40.40" description = "Low-level, data-driven core of boto 3." optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "botocore-1.40.39-py3-none-any.whl", hash = "sha256:144e0e887a9fc198c6772f660fc006028bd1a9ce5eea3caddd848db3e421bc79"}, - {file = "botocore-1.40.39.tar.gz", hash = "sha256:c6efc55cac341811ba90c693d20097db6e2ce903451d94496bccd3f672b1709d"}, + {file = "botocore-1.40.40-py3-none-any.whl", hash = "sha256:68506142b3cde93145ef3ee0268f2444f2b68ada225a151f714092bbd3d6516a"}, + {file = "botocore-1.40.40.tar.gz", hash = "sha256:78eb121a16a6481ed0f6e1aebe53a4f23aa121f34466846c13a5ca48fa980e31"}, ] [package.dependencies] diff --git a/apps/user-service/src/main/java/site/icebang/domain/workflow/runner/fastapi/body/BlogRagBodyBuilder.java b/apps/user-service/src/main/java/site/icebang/domain/workflow/runner/fastapi/body/BlogRagBodyBuilder.java index 8a8008ed..ed528629 100644 --- a/apps/user-service/src/main/java/site/icebang/domain/workflow/runner/fastapi/body/BlogRagBodyBuilder.java +++ b/apps/user-service/src/main/java/site/icebang/domain/workflow/runner/fastapi/body/BlogRagBodyBuilder.java @@ -20,7 +20,8 @@ public class BlogRagBodyBuilder implements TaskBodyBuilder { private final ObjectMapper objectMapper; private static final String TASK_NAME = "블로그 RAG 생성 태스크"; private static final String KEYWORD_SOURCE_TASK = "키워드 검색 태스크"; - private static final String PRODUCT_SELECT_SOURCE_TASK = "상품 선택 태스크"; // 변경: S3 업로드 → 상품 선택 + private static final String PRODUCT_SELECT_SOURCE_TASK = "상품 선택 태스크"; + private static final String OCR_SOURCE_TASK = "이미지 OCR 태스크"; @Override public boolean supports(String taskName) { @@ -36,14 +37,17 @@ public ObjectNode build(Task task, Map workflowContext) { .map(node -> node.path("data").path("keyword")) .ifPresent(keywordNode -> body.set("keyword", keywordNode)); + // OCR 번역 결과 가져오기 (새로 추가) + Optional.ofNullable(workflowContext.get(OCR_SOURCE_TASK)) + .map(node -> node.path("data").path("translation_language")) + .filter(node -> !node.isMissingNode() && !node.asText().trim().isEmpty()) + .ifPresent(translationNode -> body.set("translation_language", translationNode)); + + // 선택된 상품 정보 가져오기 Optional.ofNullable(workflowContext.get(PRODUCT_SELECT_SOURCE_TASK)) .map(node -> node.path("data").path("selected_product")) .ifPresent(productNode -> body.set("product_info", productNode)); - // 기본 콘텐츠 설정 - body.put("content_type", "review_blog"); - body.put("target_length", 1000); - return body; } } diff --git a/apps/user-service/src/main/java/site/icebang/domain/workflow/runner/fastapi/body/ImageOcrBodyBuilder.java b/apps/user-service/src/main/java/site/icebang/domain/workflow/runner/fastapi/body/ImageOcrBodyBuilder.java new file mode 100644 index 00000000..b045c0fe --- /dev/null +++ b/apps/user-service/src/main/java/site/icebang/domain/workflow/runner/fastapi/body/ImageOcrBodyBuilder.java @@ -0,0 +1,41 @@ +package site.icebang.domain.workflow.runner.fastapi.body; + +import java.util.Map; +import java.util.Optional; + +import org.springframework.stereotype.Component; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; + +import lombok.RequiredArgsConstructor; + +import site.icebang.domain.workflow.model.Task; + +@Component +@RequiredArgsConstructor +public class ImageOcrBodyBuilder implements TaskBodyBuilder { + + private final ObjectMapper objectMapper; + private static final String TASK_NAME = "이미지 OCR 태스크"; + private static final String KEYWORD_SOURCE_TASK = "키워드 검색 태스크"; + + @Override + public boolean supports(String taskName) { + return TASK_NAME.equals(taskName); + } + + @Override + public ObjectNode build(Task task, Map workflowContext) { + ObjectNode body = objectMapper.createObjectNode(); + + // 키워드 정보 가져오기 (OCR 처리용) + Optional.ofNullable(workflowContext.get(KEYWORD_SOURCE_TASK)) + .map(node -> node.path("data").path("keyword")) + .filter(node -> !node.isMissingNode() && !node.asText().trim().isEmpty()) + .ifPresent(keywordNode -> body.set("keyword", keywordNode)); + + return body; + } +} diff --git a/apps/user-service/src/main/resources/sql/data/03-insert-workflow.sql b/apps/user-service/src/main/resources/sql/data/03-insert-workflow.sql index e7e28042..9b95977b 100644 --- a/apps/user-service/src/main/resources/sql/data/03-insert-workflow.sql +++ b/apps/user-service/src/main/resources/sql/data/03-insert-workflow.sql @@ -16,37 +16,37 @@ DELETE FROM `workflow`; -- 워크플로우 생성 (ID: 1) INSERT INTO `workflow` (`id`, `name`, `description`, `created_by`, `default_config`) VALUES (1, '상품 분석 및 블로그 자동 발행', '키워드 검색부터 상품 분석 후 블로그 발행까지의 자동화 프로세스', 1, - JSON_OBJECT('1',json_object('tag','naver'),'9',json_object('tag','blogger','blog_id', '', 'blog_pw', ''))) + JSON_OBJECT('1',json_object('tag','naver'),'10',json_object('tag','blogger','blog_id', '', 'blog_pw', ''))) ON DUPLICATE KEY UPDATE name = VALUES(name), description = VALUES(description), updated_at = UTC_TIMESTAMP(); -- Job 생성 (ID: 1, 2) INSERT INTO `job` (`id`, `name`, `description`, `created_by`) VALUES - (1, '상품 분석', '키워드 검색, 상품 크롤링 및 유사도 분석 작업', 1), - (2, '블로그 콘텐츠 생성', '분석 데이터를 기반으로 RAG 콘텐츠 생성 및 발행 작업', 1) + (1, '상품 분석', '키워드 검색, 상품 크롤링, S3 업로드, OCR 처리 및 상품 선택 작업', 1), + (2, '블로그 콘텐츠 생성', 'OCR 데이터 기반 RAG 콘텐츠 생성 및 발행 작업', 1) ON DUPLICATE KEY UPDATE name = VALUES(name), description = VALUES(description), updated_at = UTC_TIMESTAMP(); --- Task 생성 (ID: 1 ~ 9) +-- Task 생성 (ID: 1 ~ 10) INSERT INTO `task` (`id`, `name`, `type`, `parameters`) VALUES (1, '키워드 검색 태스크', 'FastAPI', JSON_OBJECT( 'endpoint', '/keywords/search', 'method', 'POST', - 'body', JSON_OBJECT('tag', 'String') -- { "tag": str } + 'body', JSON_OBJECT('tag', 'String') )), (2, '상품 검색 태스크', 'FastAPI', JSON_OBJECT( 'endpoint', '/products/search', 'method', 'POST', - 'body', JSON_OBJECT('keyword', 'String') -- { "keyword": str } + 'body', JSON_OBJECT('keyword', 'String') )), (3, '상품 매칭 태스크', 'FastAPI', JSON_OBJECT( 'endpoint', '/products/match', 'method', 'POST', - 'body', JSON_OBJECT( -- { keyword: str, search_results: List } + 'body', JSON_OBJECT( 'keyword', 'String', 'search_results', 'List' ) )), (4, '상품 유사도 분석 태스크', 'FastAPI', JSON_OBJECT( 'endpoint', '/products/similarity', 'method', 'POST', - 'body', JSON_OBJECT( -- { keyword: str, matched_products: List, search_results: List } + 'body', JSON_OBJECT( 'keyword', 'String', 'matched_products', 'List', 'search_results', 'List' @@ -54,11 +54,11 @@ INSERT INTO `task` (`id`, `name`, `type`, `parameters`) VALUES )), (5, '상품 정보 크롤링 태스크', 'FastAPI', JSON_OBJECT( 'endpoint', '/products/crawl', 'method', 'POST', - 'body', JSON_OBJECT('product_urls', 'List') -- { "product_urls": List[str] } 수정됨 + 'body', JSON_OBJECT('product_urls', 'List') )), (6, 'S3 업로드 태스크', 'FastAPI', JSON_OBJECT( 'endpoint', '/products/s3-upload', 'method', 'POST', - 'body', JSON_OBJECT( -- { keyword: str, crawled_products: List, base_folder: str } + 'body', JSON_OBJECT( 'keyword', 'String', 'crawled_products', 'List', 'base_folder', 'String' @@ -66,16 +66,26 @@ INSERT INTO `task` (`id`, `name`, `type`, `parameters`) VALUES )), (7, '상품 선택 태스크', 'FastAPI', JSON_OBJECT( 'endpoint', '/products/select', 'method', 'POST', - 'body', JSON_OBJECT( -- { task_run_id: int, selection_criteria: str } + 'body', JSON_OBJECT( 'task_run_id', 'Integer', 'selection_criteria', 'String' ) )), - -- RAG관련 request body는 추후에 결정될 예정 - (8, '블로그 RAG 생성 태스크', 'FastAPI', JSON_OBJECT('endpoint', '/blogs/rag/create', 'method', 'POST')), - (9, '블로그 발행 태스크', 'FastAPI', JSON_OBJECT( + (8, '이미지 OCR 태스크', 'FastAPI', JSON_OBJECT( + 'endpoint', '/blogs/ocr/extract', 'method', 'POST', + 'body', JSON_OBJECT('keyword', 'String') + )), + (9, '블로그 RAG 생성 태스크', 'FastAPI', JSON_OBJECT( + 'endpoint', '/blogs/rag/create', 'method', 'POST', + 'body', JSON_OBJECT( + 'keyword', 'String', + 'translation_language', 'String', + 'product_info', 'Object' + ) + )), + (10, '블로그 발행 태스크', 'FastAPI', JSON_OBJECT( 'endpoint', '/blogs/publish', 'method', 'POST', - 'body', JSON_OBJECT( -- { tag: str, blog_id: str, ... } + 'body', JSON_OBJECT( 'tag', 'String', 'blog_id', 'String', 'blog_pw', 'String', @@ -84,7 +94,7 @@ INSERT INTO `task` (`id`, `name`, `type`, `parameters`) VALUES 'post_content', 'String', 'post_tags', 'List' ) - )) + )) ON DUPLICATE KEY UPDATE name = VALUES(name), type = VALUES(type), parameters = VALUES(parameters), updated_at = UTC_TIMESTAMP(); -- =================================================================== @@ -98,10 +108,10 @@ INSERT INTO `workflow_job` (`workflow_id`, `job_id`, `execution_order`) VALUES -- Job-Task 연결 INSERT INTO `job_task` (`job_id`, `task_id`, `execution_order`) VALUES - -- Job 1: 상품 분석 (키워드검색 → 상품검색 → 매칭 → 유사도 → 크롤링 → S3업로드 → 상품선택) - (1, 1, 1), (1, 2, 2), (1, 3, 3), (1, 4, 4), (1, 5, 5), (1, 6, 6), (1, 7, 7), + -- Job 1: 상품 분석 (키워드검색 → 상품검색 → 매칭 → 유사도 → 크롤링 → S3업로드 → 상품선택 → OCR) + (1, 1, 1), (1, 2, 2), (1, 3, 3), (1, 4, 4), (1, 5, 5), (1, 6, 6), (1, 7, 7), (1, 8, 8), -- Job 2: 블로그 콘텐츠 생성 (RAG생성 → 발행) - (2, 8, 1), (2, 9, 2) + (2, 9, 1), (2, 10, 2) ON DUPLICATE KEY UPDATE execution_order = VALUES(execution_order); -- 스케줄 설정 (매일 오전 8시)