mirror of
https://github.com/won-ktds/smarketing-backend.git
synced 2025-12-06 07:06:24 +00:00
Merge branch 'main' of https://github.com/won-ktds/smarketing-backend into poster-content
This commit is contained in:
commit
9004f7b726
36
smarketing-ai/deployment/Jenkinsfile
vendored
36
smarketing-ai/deployment/Jenkinsfile
vendored
@ -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') {
|
||||
|
||||
@ -1380,7 +1380,7 @@ class SnsContentService:
|
||||
'call_to_action': ['방문', '예약', '문의', '공감', '이웃추가'],
|
||||
'image_placement_strategy': [
|
||||
'매장 외관 → 인테리어 → 메뉴판 → 음식 → 분위기',
|
||||
'텍스트 2-3문장마다 이미지 배치',
|
||||
##'텍스트 2-3문장마다 이미지 배치',
|
||||
'이미지 설명은 간결하고 매력적으로',
|
||||
'마지막에 대표 이미지로 마무리'
|
||||
]
|
||||
@ -1713,6 +1713,11 @@ class SnsContentService:
|
||||
5. 줄바꿈을 활용하여 가독성 향상
|
||||
6. 해시태그는 본문과 자연스럽게 연결되도록 배치
|
||||
|
||||
**이미지 태그 사용법:**
|
||||
- [IMAGE_1]: 첫 번째 이미지 배치 위치
|
||||
- [IMAGE_2]: 두 번째 이미지 배치 위치
|
||||
- 각 이미지 태그 다음 줄에 이미지 설명 문구 작성
|
||||
|
||||
**필수 요구사항:**
|
||||
{request.requirement} or '고객의 관심을 끌고 방문을 유도하는 매력적인 게시물'
|
||||
|
||||
@ -1777,21 +1782,14 @@ class SnsContentService:
|
||||
1. 검색자의 궁금증을 해결하는 정보 중심 작성
|
||||
2. 구체적인 가격, 위치, 운영시간 등 실용 정보 포함
|
||||
3. 개인적인 경험과 솔직한 후기 작성
|
||||
4. 각 섹션마다 적절한 위치에 [IMAGE_X] 태그로 이미지 배치 위치 표시
|
||||
5. 이미지마다 간단한 설명 문구 추가
|
||||
6. 지역 정보와 접근성 정보 포함
|
||||
|
||||
**이미지 태그 사용법:**
|
||||
- [IMAGE_1]: 첫 번째 이미지 배치 위치
|
||||
- [IMAGE_2]: 두 번째 이미지 배치 위치
|
||||
- 각 이미지 태그 다음 줄에 이미지 설명 문구 작성
|
||||
4. 이미지마다 간단한 설명 문구 추가
|
||||
5. 지역 정보와 접근성 정보 포함
|
||||
|
||||
**필수 요구사항:**
|
||||
{request.requirement} or '유용한 정보를 제공하여 방문을 유도하는 신뢰성 있는 후기'
|
||||
|
||||
네이버 검색에서 상위 노출되고, 실제로 도움이 되는 정보를 제공하는 블로그 포스트를 작성해주세요.
|
||||
필수 요구사항을 반드시 참고하여 작성해주세요.
|
||||
이미지 배치 위치를 [IMAGE_X] 태그로 명확히 표시해주세요.
|
||||
"""
|
||||
return prompt
|
||||
|
||||
|
||||
@ -43,7 +43,11 @@ 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);
|
||||
|
||||
|
||||
32
smarketing-java/deployment/Jenkinsfile
vendored
32
smarketing-java/deployment/Jenkinsfile
vendored
@ -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') {
|
||||
|
||||
@ -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
|
||||
@ -462,84 +373,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
|
||||
@ -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
|
||||
|
||||
@ -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())
|
||||
|
||||
@ -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 콘텐츠 생성
|
||||
*
|
||||
@ -44,8 +48,10 @@ public class SnsContentService implements SnsContentUseCase {
|
||||
@Transactional
|
||||
public SnsContentCreateResponse generateSnsContent(SnsContentCreateRequest request, List<MultipartFile> files) {
|
||||
//파일들 주소 가져옴
|
||||
List<String> urls = blobStorageService.uploadImage(files, "containerName");
|
||||
if(files != null) {
|
||||
List<String> urls = blobStorageService.uploadImage(files, contentImageContainer);
|
||||
request.setImages(urls);
|
||||
}
|
||||
|
||||
// AI를 사용하여 SNS 콘텐츠 생성
|
||||
String content = aiContentGenerator.generateSnsContent(request);
|
||||
@ -67,8 +73,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 +80,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())
|
||||
|
||||
@ -17,7 +17,7 @@ public enum Platform {
|
||||
FACEBOOK("페이스북"),
|
||||
KAKAO_STORY("카카오스토리"),
|
||||
YOUTUBE("유튜브"),
|
||||
GENERAL("일반");
|
||||
POSTER("포스터");
|
||||
|
||||
private final String displayName;
|
||||
|
||||
|
||||
@ -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<ApiResponse<SnsContentCreateResponse>> generateSnsContent(@Valid @RequestPart("request") String requestJson,
|
||||
@Valid @RequestPart("files") List<MultipartFile> images) throws JsonProcessingException {
|
||||
@Valid @RequestPart(name = "files", required = false) List<MultipartFile> images) throws JsonProcessingException {
|
||||
SnsContentCreateRequest request = objectMapper.readValue(requestJson, SnsContentCreateRequest.class);
|
||||
SnsContentCreateResponse response = snsContentUseCase.generateSnsContent(request, images);
|
||||
return ResponseEntity.ok(ApiResponse.success(response, "SNS 콘텐츠가 성공적으로 생성되었습니다."));
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user