From 8ec1625e14b66b0bfbeb42d59b1358b9326f9da2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=84=9C=EC=9D=80?= <61147083+BangSun98@users.noreply.github.com> Date: Wed, 18 Jun 2025 14:26:35 +0900 Subject: [PATCH 01/17] change from GENRAL to POSTER --- .../content/application/service/PosterContentService.java | 2 +- .../content/application/service/SnsContentService.java | 3 --- .../java/com/won/smarketing/content/domain/model/Platform.java | 2 +- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/smarketing-java/marketing-content/src/main/java/com/won/smarketing/content/application/service/PosterContentService.java b/smarketing-java/marketing-content/src/main/java/com/won/smarketing/content/application/service/PosterContentService.java index 69c6213..a676c40 100644 --- a/smarketing-java/marketing-content/src/main/java/com/won/smarketing/content/application/service/PosterContentService.java +++ b/smarketing-java/marketing-content/src/main/java/com/won/smarketing/content/application/service/PosterContentService.java @@ -84,7 +84,7 @@ public class PosterContentService implements PosterContentUseCase { // 콘텐츠 엔티티 생성 Content content = Content.builder() .contentType(ContentType.POSTER) - .platform(Platform.GENERAL) + .platform(Platform.POSTER) .title(request.getTitle()) .content(request.getContent()) .images(request.getImages()) diff --git a/smarketing-java/marketing-content/src/main/java/com/won/smarketing/content/application/service/SnsContentService.java b/smarketing-java/marketing-content/src/main/java/com/won/smarketing/content/application/service/SnsContentService.java index 6119226..753063b 100644 --- a/smarketing-java/marketing-content/src/main/java/com/won/smarketing/content/application/service/SnsContentService.java +++ b/smarketing-java/marketing-content/src/main/java/com/won/smarketing/content/application/service/SnsContentService.java @@ -67,8 +67,6 @@ public class SnsContentService implements SnsContentUseCase { CreationConditions conditions = CreationConditions.builder() .category(request.getCategory()) .requirement(request.getRequirement()) - //.toneAndManner(request.getToneAndManner()) - //.emotionIntensity(request.getEmotionIntensity()) .eventName(request.getEventName()) .startDate(request.getStartDate()) .endDate(request.getEndDate()) @@ -76,7 +74,6 @@ public class SnsContentService implements SnsContentUseCase { // 콘텐츠 엔티티 생성 및 저장 Content content = Content.builder() -// .contentType(ContentType.SNS_POST) .platform(Platform.fromString(request.getPlatform())) .title(request.getTitle()) .content(request.getContent()) diff --git a/smarketing-java/marketing-content/src/main/java/com/won/smarketing/content/domain/model/Platform.java b/smarketing-java/marketing-content/src/main/java/com/won/smarketing/content/domain/model/Platform.java index 66e266c..aea9446 100644 --- a/smarketing-java/marketing-content/src/main/java/com/won/smarketing/content/domain/model/Platform.java +++ b/smarketing-java/marketing-content/src/main/java/com/won/smarketing/content/domain/model/Platform.java @@ -17,7 +17,7 @@ public enum Platform { FACEBOOK("페이스북"), KAKAO_STORY("카카오스토리"), YOUTUBE("유튜브"), - GENERAL("일반"); + POSTER("포스터"); private final String displayName; From b6721846476f73f1ebcd2ed68fbd56db9a98ecdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=84=9C=EC=9D=80?= <61147083+BangSun98@users.noreply.github.com> Date: Wed, 18 Jun 2025 14:43:36 +0900 Subject: [PATCH 02/17] add sns_contents prompt --- smarketing-ai/services/sns_content_service.py | 1 + 1 file changed, 1 insertion(+) diff --git a/smarketing-ai/services/sns_content_service.py b/smarketing-ai/services/sns_content_service.py index 16fd8ba..7554474 100644 --- a/smarketing-ai/services/sns_content_service.py +++ b/smarketing-ai/services/sns_content_service.py @@ -1792,6 +1792,7 @@ class SnsContentService: 네이버 검색에서 상위 노출되고, 실제로 도움이 되는 정보를 제공하는 블로그 포스트를 작성해주세요. 필수 요구사항을 반드시 참고하여 작성해주세요. 이미지 배치 위치를 [IMAGE_X] 태그로 명확히 표시해주세요. +이미지는 제공된 것만 사용하시고, 추가하진 말아주세요. """ return prompt From 5a1cdfbd8af4e936f813eadba4f11ff5dd2931f0 Mon Sep 17 00:00:00 2001 From: yuhalog Date: Wed, 18 Jun 2025 14:45:53 +0900 Subject: [PATCH 03/17] fix: Security Config --- .../java/com/won/smarketing/common/config/SecurityConfig.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/smarketing-java/common/src/main/java/com/won/smarketing/common/config/SecurityConfig.java b/smarketing-java/common/src/main/java/com/won/smarketing/common/config/SecurityConfig.java index 89f1436..b5179bb 100644 --- a/smarketing-java/common/src/main/java/com/won/smarketing/common/config/SecurityConfig.java +++ b/smarketing-java/common/src/main/java/com/won/smarketing/common/config/SecurityConfig.java @@ -45,7 +45,8 @@ public class SecurityConfig { .authorizeHttpRequests(auth -> auth .anyRequest().permitAll() ) - .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); +// .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class) + ; return http.build(); } From 4e19db7c684fc47639a27c0293a0aa064e126e3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=84=9C=EC=9D=80?= <61147083+BangSun98@users.noreply.github.com> Date: Wed, 18 Jun 2025 14:57:03 +0900 Subject: [PATCH 04/17] change images required false --- .../content/presentation/controller/ContentController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smarketing-java/marketing-content/src/main/java/com/won/smarketing/content/presentation/controller/ContentController.java b/smarketing-java/marketing-content/src/main/java/com/won/smarketing/content/presentation/controller/ContentController.java index ab7afa1..bb4bfc6 100644 --- a/smarketing-java/marketing-content/src/main/java/com/won/smarketing/content/presentation/controller/ContentController.java +++ b/smarketing-java/marketing-content/src/main/java/com/won/smarketing/content/presentation/controller/ContentController.java @@ -46,7 +46,7 @@ public class ContentController { @Operation(summary = "SNS 게시물 생성", description = "AI를 활용하여 SNS 게시물을 생성합니다.") @PostMapping(path = "/sns/generate", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public ResponseEntity> generateSnsContent(@Valid @RequestPart("request") String requestJson, - @Valid @RequestPart("files") List images) throws JsonProcessingException { + @Valid @RequestPart(name = "files", required = false) List images) throws JsonProcessingException { SnsContentCreateRequest request = objectMapper.readValue(requestJson, SnsContentCreateRequest.class); SnsContentCreateResponse response = snsContentUseCase.generateSnsContent(request, images); return ResponseEntity.ok(ApiResponse.success(response, "SNS 콘텐츠가 성공적으로 생성되었습니다.")); From 09a637337115ab64766838de1cb644c388d2ae5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=84=9C=EC=9D=80?= <61147083+BangSun98@users.noreply.github.com> Date: Wed, 18 Jun 2025 15:06:32 +0900 Subject: [PATCH 05/17] add if --- .../content/application/service/SnsContentService.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/smarketing-java/marketing-content/src/main/java/com/won/smarketing/content/application/service/SnsContentService.java b/smarketing-java/marketing-content/src/main/java/com/won/smarketing/content/application/service/SnsContentService.java index 3332fbf..86c279c 100644 --- a/smarketing-java/marketing-content/src/main/java/com/won/smarketing/content/application/service/SnsContentService.java +++ b/smarketing-java/marketing-content/src/main/java/com/won/smarketing/content/application/service/SnsContentService.java @@ -44,8 +44,10 @@ public class SnsContentService implements SnsContentUseCase { @Transactional public SnsContentCreateResponse generateSnsContent(SnsContentCreateRequest request, List files) { //파일들 주소 가져옴 - List urls = blobStorageService.uploadImage(files, "containerName"); - request.setImages(urls); + if(files != null) { + List urls = blobStorageService.uploadImage(files, "containerName"); + request.setImages(urls); + } // AI를 사용하여 SNS 콘텐츠 생성 String content = aiContentGenerator.generateSnsContent(request); From 5c6b5a732b854c428e00b080d0dccb05db37e1b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=84=9C=EC=9D=80?= <61147083+BangSun98@users.noreply.github.com> Date: Wed, 18 Jun 2025 15:09:57 +0900 Subject: [PATCH 06/17] chg option --- smarketing-ai/services/sns_content_service.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smarketing-ai/services/sns_content_service.py b/smarketing-ai/services/sns_content_service.py index 7554474..2f0b98d 100644 --- a/smarketing-ai/services/sns_content_service.py +++ b/smarketing-ai/services/sns_content_service.py @@ -1380,7 +1380,7 @@ class SnsContentService: 'call_to_action': ['방문', '예약', '문의', '공감', '이웃추가'], 'image_placement_strategy': [ '매장 외관 → 인테리어 → 메뉴판 → 음식 → 분위기', - '텍스트 2-3문장마다 이미지 배치', + ##'텍스트 2-3문장마다 이미지 배치', '이미지 설명은 간결하고 매력적으로', '마지막에 대표 이미지로 마무리' ] From aed79a7f966b3812a594fbec94c98871bec02e38 Mon Sep 17 00:00:00 2001 From: yuhalog Date: Wed, 18 Jun 2025 15:13:48 +0900 Subject: [PATCH 07/17] refactor: Spring security config --- .../com/won/smarketing/common/config/SecurityConfig.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/smarketing-java/common/src/main/java/com/won/smarketing/common/config/SecurityConfig.java b/smarketing-java/common/src/main/java/com/won/smarketing/common/config/SecurityConfig.java index b5179bb..dcc1f20 100644 --- a/smarketing-java/common/src/main/java/com/won/smarketing/common/config/SecurityConfig.java +++ b/smarketing-java/common/src/main/java/com/won/smarketing/common/config/SecurityConfig.java @@ -43,10 +43,13 @@ public class SecurityConfig { .cors(cors -> cors.configurationSource(corsConfigurationSource())) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authorizeHttpRequests(auth -> auth - .anyRequest().permitAll() + .requestMatchers("/api/auth/**", "/api/member/register", "/api/member/check-duplicate/**", + "/api/member/validate-password", "/swagger-ui/**", "/v3/api-docs/**", + "/swagger-resources/**", "/webjars/**", "/actuator/**", "/health/**", "/error" + ).permitAll() + .anyRequest().authenticated() ) -// .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class) - ; + .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); return http.build(); } From 17e6235c99e372436d38bf8a027b6323b6f78275 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=84=9C=EC=9D=80?= <61147083+BangSun98@users.noreply.github.com> Date: Wed, 18 Jun 2025 15:35:02 +0900 Subject: [PATCH 08/17] set image container & chg blog --- smarketing-ai/services/sns_content_service.py | 188 +++++++++--------- .../service/SnsContentService.java | 6 +- 2 files changed, 99 insertions(+), 95 deletions(-) diff --git a/smarketing-ai/services/sns_content_service.py b/smarketing-ai/services/sns_content_service.py index 2f0b98d..f8e837f 100644 --- a/smarketing-ai/services/sns_content_service.py +++ b/smarketing-ai/services/sns_content_service.py @@ -1450,8 +1450,8 @@ class SnsContentService: # 네이버 블로그인 경우 이미지 배치 계획 생성 image_placement_plan = None - if request.platform == '네이버 블로그': - image_placement_plan = self._create_image_placement_plan(image_analysis, request) + # if request.platform == '네이버 블로그': + # image_placement_plan = self._create_image_placement_plan(image_analysis, request) # 플랫폼별 특화 프롬프트 생성 prompt = self._create_platform_specific_prompt(request, image_analysis, image_placement_plan) @@ -1551,98 +1551,98 @@ class SnsContentService: return '기타' - def _create_image_placement_plan(self, image_analysis: Dict[str, Any], request: SnsContentGetRequest) -> Dict[ - str, Any]: - """ - 네이버 블로그용 이미지 배치 계획 생성 - """ - images = image_analysis.get('results', []) - if not images: - return None - - # 이미지 타입별 분류 - categorized_images = { - '매장외관': [], - '인테리어': [], - '메뉴판': [], - '음식': [], - '사람': [], - '기타': [] - } - - for img in images: - img_type = img.get('type', '기타') - categorized_images[img_type].append(img) - - # 블로그 구조에 따른 이미지 배치 계획 - placement_plan = { - 'structure': [ - { - 'section': '인트로', - 'description': '첫인상과 방문 동기', - 'recommended_images': [], - 'placement_guide': '매장 외관이나 대표적인 음식 사진으로 시작' - }, - { - 'section': '매장 정보', - 'description': '위치, 분위기, 인테리어 소개', - 'recommended_images': [], - 'placement_guide': '매장 외관 → 내부 인테리어 순서로 배치' - }, - { - 'section': '메뉴 소개', - 'description': '주문한 메뉴와 상세 후기', - 'recommended_images': [], - 'placement_guide': '메뉴판 → 실제 음식 사진 순서로 배치' - }, - { - 'section': '총평', - 'description': '재방문 의향과 추천 이유', - 'recommended_images': [], - 'placement_guide': '가장 매력적인 음식 사진이나 전체 분위기 사진' - } - ], - 'image_sequence': [], - 'usage_guide': [] - } - - # 각 섹션에 적절한 이미지 배정 - # 인트로: 매장외관 또는 대표 음식 - if categorized_images['매장외관']: - placement_plan['structure'][0]['recommended_images'].extend(categorized_images['매장외관'][:1]) - elif categorized_images['음식']: - placement_plan['structure'][0]['recommended_images'].extend(categorized_images['음식'][:1]) - - # 매장 정보: 외관 + 인테리어 - placement_plan['structure'][1]['recommended_images'].extend(categorized_images['매장외관']) - placement_plan['structure'][1]['recommended_images'].extend(categorized_images['인테리어']) - - # 메뉴 소개: 메뉴판 + 음식 - placement_plan['structure'][2]['recommended_images'].extend(categorized_images['메뉴판']) - placement_plan['structure'][2]['recommended_images'].extend(categorized_images['음식']) - - # 총평: 남은 음식 사진 또는 기타 - remaining_food = [img for img in categorized_images['음식'] - if img not in placement_plan['structure'][2]['recommended_images']] - placement_plan['structure'][3]['recommended_images'].extend(remaining_food[:1]) - placement_plan['structure'][3]['recommended_images'].extend(categorized_images['기타'][:1]) - - # 전체 이미지 순서 생성 - for section in placement_plan['structure']: - for img in section['recommended_images']: - if img not in placement_plan['image_sequence']: - placement_plan['image_sequence'].append(img) - - # 사용 가이드 생성 - placement_plan['usage_guide'] = [ - "📸 이미지 배치 가이드라인:", - "1. 각 섹션마다 2-3문장의 설명 후 이미지 삽입", - "2. 이미지마다 간단한 설명 텍스트 추가", - "3. 음식 사진은 가장 맛있어 보이는 각도로 배치", - "4. 마지막에 전체적인 분위기를 보여주는 사진으로 마무리" - ] - - return placement_plan + # def _create_image_placement_plan(self, image_analysis: Dict[str, Any], request: SnsContentGetRequest) -> Dict[ + # str, Any]: + # """ + # 네이버 블로그용 이미지 배치 계획 생성 + # """ + # images = image_analysis.get('results', []) + # if not images: + # return None + # + # # 이미지 타입별 분류 + # categorized_images = { + # '매장외관': [], + # '인테리어': [], + # '메뉴판': [], + # '음식': [], + # '사람': [], + # '기타': [] + # } + # + # for img in images: + # img_type = img.get('type', '기타') + # categorized_images[img_type].append(img) + # + # # 블로그 구조에 따른 이미지 배치 계획 + # #placement_plan = { + # # 'structure': [ + # # { + # # 'section': '인트로', + # # 'description': '첫인상과 방문 동기', + # # 'recommended_images': [], + # # 'placement_guide': '매장 외관이나 대표적인 음식 사진으로 시작' + # # }, + # # { + # # 'section': '매장 정보', + # # 'description': '위치, 분위기, 인테리어 소개', + # # 'recommended_images': [], + # # 'placement_guide': '매장 외관 → 내부 인테리어 순서로 배치' + # # }, + # # { + # # 'section': '메뉴 소개', + # # 'description': '주문한 메뉴와 상세 후기', + # # 'recommended_images': [], + # # 'placement_guide': '메뉴판 → 실제 음식 사진 순서로 배치' + # # }, + # # { + # # 'section': '총평', + # # 'description': '재방문 의향과 추천 이유', + # # 'recommended_images': [], + # # 'placement_guide': '가장 매력적인 음식 사진이나 전체 분위기 사진' + # # } + # # ], + # # 'image_sequence': [], + # # 'usage_guide': [] + # # } + # + # # 각 섹션에 적절한 이미지 배정 + # # 인트로: 매장외관 또는 대표 음식 + # if categorized_images['매장외관']: + # placement_plan['structure'][0]['recommended_images'].extend(categorized_images['매장외관'][:1]) + # elif categorized_images['음식']: + # placement_plan['structure'][0]['recommended_images'].extend(categorized_images['음식'][:1]) + # + # # 매장 정보: 외관 + 인테리어 + # placement_plan['structure'][1]['recommended_images'].extend(categorized_images['매장외관']) + # placement_plan['structure'][1]['recommended_images'].extend(categorized_images['인테리어']) + # + # # 메뉴 소개: 메뉴판 + 음식 + # placement_plan['structure'][2]['recommended_images'].extend(categorized_images['메뉴판']) + # placement_plan['structure'][2]['recommended_images'].extend(categorized_images['음식']) + # + # # 총평: 남은 음식 사진 또는 기타 + # remaining_food = [img for img in categorized_images['음식'] + # if img not in placement_plan['structure'][2]['recommended_images']] + # placement_plan['structure'][3]['recommended_images'].extend(remaining_food[:1]) + # placement_plan['structure'][3]['recommended_images'].extend(categorized_images['기타'][:1]) + # + # # 전체 이미지 순서 생성 + # for section in placement_plan['structure']: + # for img in section['recommended_images']: + # if img not in placement_plan['image_sequence']: + # placement_plan['image_sequence'].append(img) + # + # # 사용 가이드 생성 + # placement_plan['usage_guide'] = [ + # "📸 이미지 배치 가이드라인:", + # "1. 각 섹션마다 2-3문장의 설명 후 이미지 삽입", + # "2. 이미지마다 간단한 설명 텍스트 추가", + # "3. 음식 사진은 가장 맛있어 보이는 각도로 배치", + # "4. 마지막에 전체적인 분위기를 보여주는 사진으로 마무리" + # ] + # + # return placement_plan def _create_platform_specific_prompt(self, request: SnsContentGetRequest, image_analysis: Dict[str, Any], image_placement_plan: Dict[str, Any] = None) -> str: diff --git a/smarketing-java/marketing-content/src/main/java/com/won/smarketing/content/application/service/SnsContentService.java b/smarketing-java/marketing-content/src/main/java/com/won/smarketing/content/application/service/SnsContentService.java index 86c279c..2aa51d0 100644 --- a/smarketing-java/marketing-content/src/main/java/com/won/smarketing/content/application/service/SnsContentService.java +++ b/smarketing-java/marketing-content/src/main/java/com/won/smarketing/content/application/service/SnsContentService.java @@ -14,6 +14,7 @@ import com.won.smarketing.content.presentation.dto.SnsContentCreateRequest; import com.won.smarketing.content.presentation.dto.SnsContentCreateResponse; import com.won.smarketing.content.presentation.dto.SnsContentSaveRequest; import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; @@ -34,6 +35,9 @@ public class SnsContentService implements SnsContentUseCase { private final AiContentGenerator aiContentGenerator; private final BlobStorageService blobStorageService; + @Value("${azure.storage.container.poster-images:content-images}") + private String contentImageContainer; + /** * SNS 콘텐츠 생성 * @@ -45,7 +49,7 @@ public class SnsContentService implements SnsContentUseCase { public SnsContentCreateResponse generateSnsContent(SnsContentCreateRequest request, List files) { //파일들 주소 가져옴 if(files != null) { - List urls = blobStorageService.uploadImage(files, "containerName"); + List urls = blobStorageService.uploadImage(files, contentImageContainer); request.setImages(urls); } From 69eaf6d8bd6da3cd991f96b2535e5a6c95b78bf5 Mon Sep 17 00:00:00 2001 From: John Hanzu Kim Date: Wed, 18 Jun 2025 15:38:42 +0900 Subject: [PATCH 09/17] Update deploy.yaml.template --- .../deployment/deploy.yaml.template | 70 ++++--------------- 1 file changed, 15 insertions(+), 55 deletions(-) diff --git a/smarketing-java/deployment/deploy.yaml.template b/smarketing-java/deployment/deploy.yaml.template index 2a2defd..15bd41d 100644 --- a/smarketing-java/deployment/deploy.yaml.template +++ b/smarketing-java/deployment/deploy.yaml.template @@ -462,84 +462,44 @@ spec: type: ClusterIP --- -# deploy.yaml.template의 Ingress 부분 - 완전한 설정 apiVersion: networking.k8s.io/v1 kind: Ingress metadata: - name: smarketing-backend - namespace: ${namespace} + name: smarketing-ingress annotations: kubernetes.io/ingress.class: nginx - nginx.ingress.kubernetes.io/rewrite-target: /$2 - nginx.ingress.kubernetes.io/ssl-redirect: "false" - nginx.ingress.kubernetes.io/use-regex: "true" + # nginx.ingress.kubernetes.io/rewrite-target: /$2 # 이 줄 제거 + # nginx.ingress.kubernetes.io/use-regex: "true" # 이 줄 제거 spec: - ingressClassName: nginx rules: - host: smarketing.20.249.184.228.nip.io http: paths: - # Member 서비스 - 인증 관련 - - path: /api/auth(/|$)(.*) - pathType: ImplementationSpecific + - path: /api/auth + pathType: Prefix backend: service: - name: member + name: auth-service port: number: 80 - # Member 서비스 - 회원 관리 (누락된 경로!) - - path: /api/member(/|$)(.*) - pathType: ImplementationSpecific + - path: /api/store + pathType: Prefix backend: service: - name: member + name: store-service port: number: 80 - # Store 서비스 - - path: /api/store(/|$)(.*) - pathType: ImplementationSpecific + - path: /api/content + pathType: Prefix backend: service: - name: store + name: content-service port: number: 80 - # Store 서비스 - 매출 관련 - - path: /api/sales(/|$)(.*) - pathType: ImplementationSpecific + - path: /api/recommend + pathType: Prefix backend: service: - name: store - port: - number: 80 - # Store 서비스 - 메뉴 관련 - - path: /api/menu(/|$)(.*) - pathType: ImplementationSpecific - backend: - service: - name: store - port: - number: 80 - # Store 서비스 - 이미지 업로드 - - path: /api/images(/|$)(.*) - pathType: ImplementationSpecific - backend: - service: - name: store - port: - number: 80 - # Marketing Content 서비스 - - path: /api/content(/|$)(.*) - pathType: ImplementationSpecific - backend: - service: - name: marketing-content - port: - number: 80 - # AI Recommend 서비스 - - path: /api/recommend(/|$)(.*) - pathType: ImplementationSpecific - backend: - service: - name: ai-recommend + name: recommend-service port: number: 80 From bab2769e0849e894f8699df6dceff4c1f6fc4976 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=84=9C=EC=9D=80?= <61147083+BangSun98@users.noreply.github.com> Date: Wed, 18 Jun 2025 15:51:55 +0900 Subject: [PATCH 10/17] chg blog setting --- smarketing-ai/services/sns_content_service.py | 195 +++++++++--------- 1 file changed, 96 insertions(+), 99 deletions(-) diff --git a/smarketing-ai/services/sns_content_service.py b/smarketing-ai/services/sns_content_service.py index f8e837f..9f35f2c 100644 --- a/smarketing-ai/services/sns_content_service.py +++ b/smarketing-ai/services/sns_content_service.py @@ -1450,8 +1450,8 @@ class SnsContentService: # 네이버 블로그인 경우 이미지 배치 계획 생성 image_placement_plan = None - # if request.platform == '네이버 블로그': - # image_placement_plan = self._create_image_placement_plan(image_analysis, request) + if request.platform == '네이버 블로그': + image_placement_plan = self._create_image_placement_plan(image_analysis, request) # 플랫폼별 특화 프롬프트 생성 prompt = self._create_platform_specific_prompt(request, image_analysis, image_placement_plan) @@ -1551,98 +1551,98 @@ class SnsContentService: return '기타' - # def _create_image_placement_plan(self, image_analysis: Dict[str, Any], request: SnsContentGetRequest) -> Dict[ - # str, Any]: - # """ - # 네이버 블로그용 이미지 배치 계획 생성 - # """ - # images = image_analysis.get('results', []) - # if not images: - # return None - # - # # 이미지 타입별 분류 - # categorized_images = { - # '매장외관': [], - # '인테리어': [], - # '메뉴판': [], - # '음식': [], - # '사람': [], - # '기타': [] - # } - # - # for img in images: - # img_type = img.get('type', '기타') - # categorized_images[img_type].append(img) - # - # # 블로그 구조에 따른 이미지 배치 계획 - # #placement_plan = { - # # 'structure': [ - # # { - # # 'section': '인트로', - # # 'description': '첫인상과 방문 동기', - # # 'recommended_images': [], - # # 'placement_guide': '매장 외관이나 대표적인 음식 사진으로 시작' - # # }, - # # { - # # 'section': '매장 정보', - # # 'description': '위치, 분위기, 인테리어 소개', - # # 'recommended_images': [], - # # 'placement_guide': '매장 외관 → 내부 인테리어 순서로 배치' - # # }, - # # { - # # 'section': '메뉴 소개', - # # 'description': '주문한 메뉴와 상세 후기', - # # 'recommended_images': [], - # # 'placement_guide': '메뉴판 → 실제 음식 사진 순서로 배치' - # # }, - # # { - # # 'section': '총평', - # # 'description': '재방문 의향과 추천 이유', - # # 'recommended_images': [], - # # 'placement_guide': '가장 매력적인 음식 사진이나 전체 분위기 사진' - # # } - # # ], - # # 'image_sequence': [], - # # 'usage_guide': [] - # # } - # - # # 각 섹션에 적절한 이미지 배정 - # # 인트로: 매장외관 또는 대표 음식 - # if categorized_images['매장외관']: - # placement_plan['structure'][0]['recommended_images'].extend(categorized_images['매장외관'][:1]) - # elif categorized_images['음식']: - # placement_plan['structure'][0]['recommended_images'].extend(categorized_images['음식'][:1]) - # - # # 매장 정보: 외관 + 인테리어 - # placement_plan['structure'][1]['recommended_images'].extend(categorized_images['매장외관']) - # placement_plan['structure'][1]['recommended_images'].extend(categorized_images['인테리어']) - # - # # 메뉴 소개: 메뉴판 + 음식 - # placement_plan['structure'][2]['recommended_images'].extend(categorized_images['메뉴판']) - # placement_plan['structure'][2]['recommended_images'].extend(categorized_images['음식']) - # - # # 총평: 남은 음식 사진 또는 기타 - # remaining_food = [img for img in categorized_images['음식'] - # if img not in placement_plan['structure'][2]['recommended_images']] - # placement_plan['structure'][3]['recommended_images'].extend(remaining_food[:1]) - # placement_plan['structure'][3]['recommended_images'].extend(categorized_images['기타'][:1]) - # - # # 전체 이미지 순서 생성 - # for section in placement_plan['structure']: - # for img in section['recommended_images']: - # if img not in placement_plan['image_sequence']: - # placement_plan['image_sequence'].append(img) - # - # # 사용 가이드 생성 - # placement_plan['usage_guide'] = [ - # "📸 이미지 배치 가이드라인:", - # "1. 각 섹션마다 2-3문장의 설명 후 이미지 삽입", - # "2. 이미지마다 간단한 설명 텍스트 추가", - # "3. 음식 사진은 가장 맛있어 보이는 각도로 배치", - # "4. 마지막에 전체적인 분위기를 보여주는 사진으로 마무리" - # ] - # - # return placement_plan + def _create_image_placement_plan(self, image_analysis: Dict[str, Any], request: SnsContentGetRequest) -> Dict[ + str, Any]: + """ + 네이버 블로그용 이미지 배치 계획 생성 + """ + images = image_analysis.get('results', []) + if not images: + return None + + # 이미지 타입별 분류 + categorized_images = { + '매장외관': [], + '인테리어': [], + '메뉴판': [], + '음식': [], + '사람': [], + '기타': [] + } + + for img in images: + img_type = img.get('type', '기타') + categorized_images[img_type].append(img) + + # 블로그 구조에 따른 이미지 배치 계획 + placement_plan = { + 'structure': [ + { + 'section': '인트로', + 'description': '첫인상과 방문 동기', + 'recommended_images': [], + 'placement_guide': '매장 외관이나 대표적인 음식 사진으로 시작' + }, + { + 'section': '매장 정보', + 'description': '위치, 분위기, 인테리어 소개', + 'recommended_images': [], + 'placement_guide': '매장 외관 → 내부 인테리어 순서로 배치' + }, + { + 'section': '메뉴 소개', + 'description': '주문한 메뉴와 상세 후기', + 'recommended_images': [], + 'placement_guide': '메뉴판 → 실제 음식 사진 순서로 배치' + }, + { + 'section': '총평', + 'description': '재방문 의향과 추천 이유', + 'recommended_images': [], + 'placement_guide': '가장 매력적인 음식 사진이나 전체 분위기 사진' + } + ], + 'image_sequence': [], + 'usage_guide': [] + } + + # 각 섹션에 적절한 이미지 배정 + # 인트로: 매장외관 또는 대표 음식 + if categorized_images['매장외관']: + placement_plan['structure'][0]['recommended_images'].extend(categorized_images['매장외관'][:1]) + elif categorized_images['음식']: + placement_plan['structure'][0]['recommended_images'].extend(categorized_images['음식'][:1]) + + # 매장 정보: 외관 + 인테리어 + placement_plan['structure'][1]['recommended_images'].extend(categorized_images['매장외관']) + placement_plan['structure'][1]['recommended_images'].extend(categorized_images['인테리어']) + + # 메뉴 소개: 메뉴판 + 음식 + placement_plan['structure'][2]['recommended_images'].extend(categorized_images['메뉴판']) + placement_plan['structure'][2]['recommended_images'].extend(categorized_images['음식']) + + # 총평: 남은 음식 사진 또는 기타 + remaining_food = [img for img in categorized_images['음식'] + if img not in placement_plan['structure'][2]['recommended_images']] + placement_plan['structure'][3]['recommended_images'].extend(remaining_food[:1]) + placement_plan['structure'][3]['recommended_images'].extend(categorized_images['기타'][:1]) + + # 전체 이미지 순서 생성 + for section in placement_plan['structure']: + for img in section['recommended_images']: + if img not in placement_plan['image_sequence']: + placement_plan['image_sequence'].append(img) + + # 사용 가이드 생성 + placement_plan['usage_guide'] = [ + "📸 이미지 배치 가이드라인:", + "1. 각 섹션마다 2-3문장의 설명 후 이미지 삽입", + "2. 이미지마다 간단한 설명 텍스트 추가", + "3. 음식 사진은 가장 맛있어 보이는 각도로 배치", + "4. 마지막에 전체적인 분위기를 보여주는 사진으로 마무리" + ] + + return placement_plan def _create_platform_specific_prompt(self, request: SnsContentGetRequest, image_analysis: Dict[str, Any], image_placement_plan: Dict[str, Any] = None) -> str: @@ -1777,9 +1777,8 @@ class SnsContentService: 1. 검색자의 궁금증을 해결하는 정보 중심 작성 2. 구체적인 가격, 위치, 운영시간 등 실용 정보 포함 3. 개인적인 경험과 솔직한 후기 작성 -4. 각 섹션마다 적절한 위치에 [IMAGE_X] 태그로 이미지 배치 위치 표시 -5. 이미지마다 간단한 설명 문구 추가 -6. 지역 정보와 접근성 정보 포함 +4. 이미지마다 간단한 설명 문구 추가 +5. 지역 정보와 접근성 정보 포함 **이미지 태그 사용법:** - [IMAGE_1]: 첫 번째 이미지 배치 위치 @@ -1791,8 +1790,6 @@ class SnsContentService: 네이버 검색에서 상위 노출되고, 실제로 도움이 되는 정보를 제공하는 블로그 포스트를 작성해주세요. 필수 요구사항을 반드시 참고하여 작성해주세요. -이미지 배치 위치를 [IMAGE_X] 태그로 명확히 표시해주세요. -이미지는 제공된 것만 사용하시고, 추가하진 말아주세요. """ return prompt From fe7f4a4b6fcbbf63376b95a4f2da03dce13af7d3 Mon Sep 17 00:00:00 2001 From: OhSeongRak Date: Wed, 18 Jun 2025 15:57:06 +0900 Subject: [PATCH 11/17] =?UTF-8?q?refactor:=20probe=20=EC=A0=84=EC=B2=B4=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../deployment/deploy.yaml.template | 95 +------------------ 1 file changed, 3 insertions(+), 92 deletions(-) diff --git a/smarketing-java/deployment/deploy.yaml.template b/smarketing-java/deployment/deploy.yaml.template index 15bd41d..c9d65a6 100644 --- a/smarketing-java/deployment/deploy.yaml.template +++ b/smarketing-java/deployment/deploy.yaml.template @@ -182,29 +182,6 @@ spec: name: common-secret - secretRef: name: member-secret - startupProbe: - tcpSocket: - port: 8081 - initialDelaySeconds: 30 - periodSeconds: 10 - timeoutSeconds: 5 - failureThreshold: 10 - livenessProbe: - httpGet: - path: /actuator/health - port: 8081 - initialDelaySeconds: 120 - periodSeconds: 30 - timeoutSeconds: 10 - failureThreshold: 3 - readinessProbe: - httpGet: - path: /actuator/health - port: 8081 - initialDelaySeconds: 60 - periodSeconds: 10 - timeoutSeconds: 5 - failureThreshold: 3 --- apiVersion: apps/v1 @@ -248,29 +225,7 @@ spec: name: common-secret - secretRef: name: store-secret - startupProbe: - tcpSocket: - port: 8082 - initialDelaySeconds: 30 - periodSeconds: 10 - timeoutSeconds: 5 - failureThreshold: 10 - livenessProbe: - httpGet: - path: /actuator/health - port: 8082 - initialDelaySeconds: 120 - periodSeconds: 30 - timeoutSeconds: 10 - failureThreshold: 3 - readinessProbe: - httpGet: - path: /actuator/health - port: 8082 - initialDelaySeconds: 60 - periodSeconds: 10 - timeoutSeconds: 5 - failureThreshold: 3 + --- apiVersion: apps/v1 @@ -314,29 +269,7 @@ spec: name: common-secret - secretRef: name: marketing-content-secret - startupProbe: - tcpSocket: - port: 8083 - initialDelaySeconds: 30 - periodSeconds: 10 - timeoutSeconds: 5 - failureThreshold: 10 - livenessProbe: - httpGet: - path: /actuator/health - port: 8083 - initialDelaySeconds: 120 - periodSeconds: 30 - timeoutSeconds: 10 - failureThreshold: 3 - readinessProbe: - httpGet: - path: /actuator/health - port: 8083 - initialDelaySeconds: 60 - periodSeconds: 10 - timeoutSeconds: 5 - failureThreshold: 3 + --- apiVersion: apps/v1 @@ -380,29 +313,7 @@ spec: name: common-secret - secretRef: name: ai-recommend-secret - startupProbe: - tcpSocket: - port: 8084 - initialDelaySeconds: 30 - periodSeconds: 10 - timeoutSeconds: 5 - failureThreshold: 10 - livenessProbe: - httpGet: - path: /actuator/health - port: 8084 - initialDelaySeconds: 120 - periodSeconds: 30 - timeoutSeconds: 10 - failureThreshold: 3 - readinessProbe: - httpGet: - path: /actuator/health - port: 8084 - initialDelaySeconds: 60 - periodSeconds: 10 - timeoutSeconds: 5 - failureThreshold: 3 + --- # Services From e969189ce7fecc2f8378c5b4cb0485c424ffad2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=84=9C=EC=9D=80?= <61147083+BangSun98@users.noreply.github.com> Date: Wed, 18 Jun 2025 16:02:08 +0900 Subject: [PATCH 12/17] delete prompt --- smarketing-ai/services/sns_content_service.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/smarketing-ai/services/sns_content_service.py b/smarketing-ai/services/sns_content_service.py index 9f35f2c..2956240 100644 --- a/smarketing-ai/services/sns_content_service.py +++ b/smarketing-ai/services/sns_content_service.py @@ -1780,11 +1780,6 @@ class SnsContentService: 4. 이미지마다 간단한 설명 문구 추가 5. 지역 정보와 접근성 정보 포함 -**이미지 태그 사용법:** -- [IMAGE_1]: 첫 번째 이미지 배치 위치 -- [IMAGE_2]: 두 번째 이미지 배치 위치 -- 각 이미지 태그 다음 줄에 이미지 설명 문구 작성 - **필수 요구사항:** {request.requirement} or '유용한 정보를 제공하여 방문을 유도하는 신뢰성 있는 후기' From 3f6a6b603446840220c8801c58f0eef1533db604 Mon Sep 17 00:00:00 2001 From: OhSeongRak Date: Wed, 18 Jun 2025 16:04:09 +0900 Subject: [PATCH 13/17] =?UTF-8?q?refactor:=20probe=20=EC=A0=84=EC=B2=B4=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- smarketing-java/deployment/deploy.yaml.template | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smarketing-java/deployment/deploy.yaml.template b/smarketing-java/deployment/deploy.yaml.template index c9d65a6..04836c2 100644 --- a/smarketing-java/deployment/deploy.yaml.template +++ b/smarketing-java/deployment/deploy.yaml.template @@ -413,4 +413,4 @@ spec: service: name: recommend-service port: - number: 80 + number: 80 \ No newline at end of file From d75f1f71fbd8bb24e8d329834ed92e0e699fe4db Mon Sep 17 00:00:00 2001 From: OhSeongRak Date: Wed, 18 Jun 2025 16:09:54 +0900 Subject: [PATCH 14/17] refactor: Jenkinsfile --- smarketing-ai/deployment/Jenkinsfile | 36 ++++++++++++++-------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/smarketing-ai/deployment/Jenkinsfile b/smarketing-ai/deployment/Jenkinsfile index e55f855..912d946 100644 --- a/smarketing-ai/deployment/Jenkinsfile +++ b/smarketing-ai/deployment/Jenkinsfile @@ -35,24 +35,24 @@ podTemplate( echo "Team ID: ${props.teamid}" } - stage("Check Changes") { - script { - def changes = sh( - script: "git diff --name-only HEAD~1 HEAD", - returnStdout: true - ).trim() - - echo "Changed files: ${changes}" - - if (!changes.contains("smarketing-ai/")) { - echo "No changes in smarketing-ai, skipping build" - currentBuild.result = 'SUCCESS' - error("Stopping pipeline - no changes detected") - } - - echo "Changes detected in smarketing-ai, proceeding with build" - } - } +// stage("Check Changes") { +// script { +// def changes = sh( +// script: "git diff --name-only HEAD~1 HEAD", +// returnStdout: true +// ).trim() +// +// echo "Changed files: ${changes}" +// +// if (!changes.contains("smarketing-ai/")) { +// echo "No changes in smarketing-ai, skipping build" +// currentBuild.result = 'SUCCESS' +// error("Stopping pipeline - no changes detected") +// } +// +// echo "Changes detected in smarketing-ai, proceeding with build" +// } +// } stage("Setup AKS") { container('azure-cli') { From 534698ca93fb45169806d29c1d8d5ac7bc9eef31 Mon Sep 17 00:00:00 2001 From: OhSeongRak Date: Wed, 18 Jun 2025 16:10:36 +0900 Subject: [PATCH 15/17] refactor: Jenkinsfile --- smarketing-java/deployment/Jenkinsfile | 32 +++++++++++++------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/smarketing-java/deployment/Jenkinsfile b/smarketing-java/deployment/Jenkinsfile index a84cf56..26ced99 100644 --- a/smarketing-java/deployment/Jenkinsfile +++ b/smarketing-java/deployment/Jenkinsfile @@ -43,22 +43,22 @@ podTemplate( echo "Image Org: ${props.image_org}" } - stage("Check Changes") { - script { - def changes = sh( - script: "git diff --name-only HEAD~1 HEAD", - returnStdout: true - ).trim() - - if (!changes.contains("smarketing-java/")) { - echo "No changes in smarketing-java, skipping build" - currentBuild.result = 'SUCCESS' - error("Stopping pipeline - no changes detected") - } - - echo "Changes detected in smarketing-java, proceeding with build" - } - } +// stage("Check Changes") { +// script { +// def changes = sh( +// script: "git diff --name-only HEAD~1 HEAD", +// returnStdout: true +// ).trim() +// +// if (!changes.contains("smarketing-java/")) { +// echo "No changes in smarketing-java, skipping build" +// currentBuild.result = 'SUCCESS' +// error("Stopping pipeline - no changes detected") +// } +// +// echo "Changes detected in smarketing-java, proceeding with build" +// } +// } stage("Setup AKS") { container('azure-cli') { From 0315e0b89de0fade8bb98fa4eb63f7299b1ae431 Mon Sep 17 00:00:00 2001 From: OhSeongRak Date: Wed, 18 Jun 2025 16:52:20 +0900 Subject: [PATCH 16/17] =?UTF-8?q?refactor:=20env=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- smarketing-java/deployment/deploy_env_vars | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smarketing-java/deployment/deploy_env_vars b/smarketing-java/deployment/deploy_env_vars index 7a5d327..6cfb19e 100644 --- a/smarketing-java/deployment/deploy_env_vars +++ b/smarketing-java/deployment/deploy_env_vars @@ -9,7 +9,7 @@ image_org=smarketing # Application Settings replicas=1 -allowed_origins=http://20.249.171.38 +allowed_origins=http://20.249.154.194 # Security Settings jwt_secret_key=8O2HQ13etL2BWZvYOiWsJ5uWFoLi6NBUG8divYVoCgtHVvlk3dqRksMl16toztDUeBTSIuOOPvHIrYq11G2BwQ From 3fdd233a70edf02fb2d5db6981338e3906bd118c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=84=9C=EC=9D=80?= <61147083+BangSun98@users.noreply.github.com> Date: Wed, 18 Jun 2025 16:54:08 +0900 Subject: [PATCH 17/17] Update sns_content_service.py --- smarketing-ai/services/sns_content_service.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/smarketing-ai/services/sns_content_service.py b/smarketing-ai/services/sns_content_service.py index 2956240..ac8bc77 100644 --- a/smarketing-ai/services/sns_content_service.py +++ b/smarketing-ai/services/sns_content_service.py @@ -1713,6 +1713,11 @@ class SnsContentService: 5. 줄바꿈을 활용하여 가독성 향상 6. 해시태그는 본문과 자연스럽게 연결되도록 배치 +**이미지 태그 사용법:** +- [IMAGE_1]: 첫 번째 이미지 배치 위치 +- [IMAGE_2]: 두 번째 이미지 배치 위치 +- 각 이미지 태그 다음 줄에 이미지 설명 문구 작성 + **필수 요구사항:** {request.requirement} or '고객의 관심을 끌고 방문을 유도하는 매력적인 게시물'