Merge branch 'main' of https://github.com/won-ktds/smarketing-backend into poster-content

This commit is contained in:
yuhalog
2025-06-18 17:00:08 +09:00
10 changed files with 78 additions and 202 deletions
@@ -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);
+16 -16
View File
@@ -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') {
+19 -148
View File
@@ -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
name: recommend-service
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
port:
number: 80
number: 80
+1 -1
View File
@@ -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");
request.setImages(urls);
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 콘텐츠가 성공적으로 생성되었습니다."));