mirror of
https://github.com/won-ktds/smarketing-backend.git
synced 2025-12-06 07:06:24 +00:00
Merge branch 'marketing-contents' of https://github.com/won-ktds/smarketing-backend into poster-content
This commit is contained in:
commit
d9ff28e7c5
@ -9,12 +9,14 @@ import com.won.smarketing.content.domain.model.CreationConditions;
|
|||||||
import com.won.smarketing.content.domain.model.Platform;
|
import com.won.smarketing.content.domain.model.Platform;
|
||||||
import com.won.smarketing.content.domain.repository.ContentRepository;
|
import com.won.smarketing.content.domain.repository.ContentRepository;
|
||||||
import com.won.smarketing.content.domain.service.AiContentGenerator;
|
import com.won.smarketing.content.domain.service.AiContentGenerator;
|
||||||
|
import com.won.smarketing.content.domain.service.BlobStorageService;
|
||||||
import com.won.smarketing.content.presentation.dto.SnsContentCreateRequest;
|
import com.won.smarketing.content.presentation.dto.SnsContentCreateRequest;
|
||||||
import com.won.smarketing.content.presentation.dto.SnsContentCreateResponse;
|
import com.won.smarketing.content.presentation.dto.SnsContentCreateResponse;
|
||||||
import com.won.smarketing.content.presentation.dto.SnsContentSaveRequest;
|
import com.won.smarketing.content.presentation.dto.SnsContentSaveRequest;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -30,6 +32,7 @@ public class SnsContentService implements SnsContentUseCase {
|
|||||||
|
|
||||||
private final ContentRepository contentRepository;
|
private final ContentRepository contentRepository;
|
||||||
private final AiContentGenerator aiContentGenerator;
|
private final AiContentGenerator aiContentGenerator;
|
||||||
|
private final BlobStorageService blobStorageService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SNS 콘텐츠 생성
|
* SNS 콘텐츠 생성
|
||||||
@ -39,14 +42,17 @@ public class SnsContentService implements SnsContentUseCase {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
public SnsContentCreateResponse generateSnsContent(SnsContentCreateRequest request) {
|
public SnsContentCreateResponse generateSnsContent(SnsContentCreateRequest request, List<MultipartFile> files) {
|
||||||
|
//파일들 주소 가져옴
|
||||||
|
List<String> urls = blobStorageService.uploadImage(files);
|
||||||
|
request.setImages(urls);
|
||||||
|
|
||||||
// AI를 사용하여 SNS 콘텐츠 생성
|
// AI를 사용하여 SNS 콘텐츠 생성
|
||||||
String content = aiContentGenerator.generateSnsContent(request);
|
String content = aiContentGenerator.generateSnsContent(request);
|
||||||
|
|
||||||
return SnsContentCreateResponse.builder()
|
return SnsContentCreateResponse.builder()
|
||||||
.content(content)
|
.content(content)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -4,6 +4,9 @@ package com.won.smarketing.content.application.usecase;
|
|||||||
import com.won.smarketing.content.presentation.dto.SnsContentCreateRequest;
|
import com.won.smarketing.content.presentation.dto.SnsContentCreateRequest;
|
||||||
import com.won.smarketing.content.presentation.dto.SnsContentCreateResponse;
|
import com.won.smarketing.content.presentation.dto.SnsContentCreateResponse;
|
||||||
import com.won.smarketing.content.presentation.dto.SnsContentSaveRequest;
|
import com.won.smarketing.content.presentation.dto.SnsContentSaveRequest;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SNS 콘텐츠 관련 UseCase 인터페이스
|
* SNS 콘텐츠 관련 UseCase 인터페이스
|
||||||
@ -16,7 +19,7 @@ public interface SnsContentUseCase {
|
|||||||
* @param request SNS 콘텐츠 생성 요청
|
* @param request SNS 콘텐츠 생성 요청
|
||||||
* @return SNS 콘텐츠 생성 응답
|
* @return SNS 콘텐츠 생성 응답
|
||||||
*/
|
*/
|
||||||
SnsContentCreateResponse generateSnsContent(SnsContentCreateRequest request);
|
SnsContentCreateResponse generateSnsContent(SnsContentCreateRequest request, List<MultipartFile> files);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SNS 콘텐츠 저장
|
* SNS 콘텐츠 저장
|
||||||
|
|||||||
@ -0,0 +1,72 @@
|
|||||||
|
// store/src/main/java/com/won/smarketing/store/config/AzureBlobStorageConfig.java
|
||||||
|
package com.won.smarketing.content.config;
|
||||||
|
|
||||||
|
import com.azure.identity.DefaultAzureCredentialBuilder;
|
||||||
|
import com.azure.storage.blob.BlobServiceClient;
|
||||||
|
import com.azure.storage.blob.BlobServiceClientBuilder;
|
||||||
|
import com.azure.storage.common.StorageSharedKeyCredential;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Azure Blob Storage 설정 클래스
|
||||||
|
* Azure Blob Storage와의 연결을 위한 설정
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
@Slf4j
|
||||||
|
public class AzureBlobStorageConfig {
|
||||||
|
|
||||||
|
@Value("${azure.storage.account-name}")
|
||||||
|
private String accountName;
|
||||||
|
|
||||||
|
@Value("${azure.storage.account-key:}")
|
||||||
|
private String accountKey;
|
||||||
|
|
||||||
|
@Value("${azure.storage.endpoint:}")
|
||||||
|
private String endpoint;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Azure Blob Storage Service Client 생성
|
||||||
|
*
|
||||||
|
* @return BlobServiceClient 인스턴스
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public BlobServiceClient blobServiceClient() {
|
||||||
|
try {
|
||||||
|
// Managed Identity 사용 시 (Azure 환경에서 권장)
|
||||||
|
if (accountKey == null || accountKey.isEmpty()) {
|
||||||
|
log.info("Azure Blob Storage 연결 - Managed Identity 사용");
|
||||||
|
return new BlobServiceClientBuilder()
|
||||||
|
.endpoint(getEndpoint())
|
||||||
|
.credential(new DefaultAzureCredentialBuilder().build())
|
||||||
|
.buildClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Account Key 사용 시 (개발 환경용)
|
||||||
|
log.info("Azure Blob Storage 연결 - Account Key 사용");
|
||||||
|
StorageSharedKeyCredential credential = new StorageSharedKeyCredential(accountName, accountKey);
|
||||||
|
return new BlobServiceClientBuilder()
|
||||||
|
.endpoint(getEndpoint())
|
||||||
|
.credential(credential)
|
||||||
|
.buildClient();
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Azure Blob Storage 클라이언트 생성 실패", e);
|
||||||
|
throw new RuntimeException("Azure Blob Storage 연결 실패", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Storage Account 엔드포인트 URL 생성
|
||||||
|
*
|
||||||
|
* @return 엔드포인트 URL
|
||||||
|
*/
|
||||||
|
private String getEndpoint() {
|
||||||
|
if (endpoint != null && !endpoint.isEmpty()) {
|
||||||
|
return endpoint;
|
||||||
|
}
|
||||||
|
return String.format("https://%s.blob.core.windows.net", accountName);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -17,7 +17,7 @@ public interface BlobStorageService {
|
|||||||
* @param file 업로드할 파일
|
* @param file 업로드할 파일
|
||||||
* @return 업로드된 파일의 URL
|
* @return 업로드된 파일의 URL
|
||||||
*/
|
*/
|
||||||
List<String> uploadImage(List<MultipartFile> file, String containerName);
|
List<String> uploadImage(List<MultipartFile> file);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -57,11 +57,10 @@ public class BlobStorageServiceImpl implements BlobStorageService {
|
|||||||
* 이미지 파일 업로드
|
* 이미지 파일 업로드
|
||||||
*
|
*
|
||||||
* @param files 업로드할 파일들
|
* @param files 업로드할 파일들
|
||||||
* @param containerName 컨테이너 이름
|
|
||||||
* @return 업로드된 파일의 URL
|
* @return 업로드된 파일의 URL
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public List<String> uploadImage(List<MultipartFile> files, String containerName) {
|
public List<String> uploadImage(List<MultipartFile> files) {
|
||||||
// 파일 유효성 검증
|
// 파일 유효성 검증
|
||||||
validateImageFile(files);
|
validateImageFile(files);
|
||||||
List<String> urls = new ArrayList<>();
|
List<String> urls = new ArrayList<>();
|
||||||
@ -71,10 +70,10 @@ public class BlobStorageServiceImpl implements BlobStorageService {
|
|||||||
for(MultipartFile file : files) {
|
for(MultipartFile file : files) {
|
||||||
String fileName = generateMenuImageFileName(file.getOriginalFilename());
|
String fileName = generateMenuImageFileName(file.getOriginalFilename());
|
||||||
|
|
||||||
ensureContainerExists(containerName);
|
ensureContainerExists(posterImageContainer);
|
||||||
|
|
||||||
// Blob 클라이언트 생성
|
// Blob 클라이언트 생성
|
||||||
BlobContainerClient containerClient = blobServiceClient.getBlobContainerClient(containerName);
|
BlobContainerClient containerClient = blobServiceClient.getBlobContainerClient(posterImageContainer);
|
||||||
BlobClient blobClient = containerClient.getBlobClient(fileName);
|
BlobClient blobClient = containerClient.getBlobClient(fileName);
|
||||||
|
|
||||||
// 파일 업로드 (간단한 방식)
|
// 파일 업로드 (간단한 방식)
|
||||||
|
|||||||
@ -41,9 +41,10 @@ public class ContentController {
|
|||||||
* @return 생성된 SNS 콘텐츠 정보
|
* @return 생성된 SNS 콘텐츠 정보
|
||||||
*/
|
*/
|
||||||
@Operation(summary = "SNS 게시물 생성", description = "AI를 활용하여 SNS 게시물을 생성합니다.")
|
@Operation(summary = "SNS 게시물 생성", description = "AI를 활용하여 SNS 게시물을 생성합니다.")
|
||||||
@PostMapping("/sns/generate")
|
@PostMapping(path = "/sns/generate", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
|
||||||
public ResponseEntity<ApiResponse<SnsContentCreateResponse>> generateSnsContent(@Valid @RequestBody SnsContentCreateRequest request) {
|
public ResponseEntity<ApiResponse<SnsContentCreateResponse>> generateSnsContent(@Valid @RequestPart SnsContentCreateRequest request,
|
||||||
SnsContentCreateResponse response = snsContentUseCase.generateSnsContent(request);
|
@RequestPart("files") List<MultipartFile> images) {
|
||||||
|
SnsContentCreateResponse response = snsContentUseCase.generateSnsContent(request, images);
|
||||||
return ResponseEntity.ok(ApiResponse.success(response, "SNS 콘텐츠가 성공적으로 생성되었습니다."));
|
return ResponseEntity.ok(ApiResponse.success(response, "SNS 콘텐츠가 성공적으로 생성되었습니다."));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -37,7 +37,15 @@ logging:
|
|||||||
external:
|
external:
|
||||||
ai-service:
|
ai-service:
|
||||||
base-url: ${AI_SERVICE_BASE_URL:http://20.249.113.247:5001}
|
base-url: ${AI_SERVICE_BASE_URL:http://20.249.113.247:5001}
|
||||||
|
azure:
|
||||||
|
storage:
|
||||||
|
account-name: ${AZURE_STORAGE_ACCOUNT_NAME:stdigitalgarage02}
|
||||||
|
account-key: ${AZURE_STORAGE_ACCOUNT_KEY:}
|
||||||
|
endpoint: ${AZURE_STORAGE_ENDPOINT:https://stdigitalgarage02.blob.core.windows.net}
|
||||||
|
container:
|
||||||
|
menu-images: ${AZURE_STORAGE_MENU_CONTAINER:smarketing-menu-images}
|
||||||
|
store-images: ${AZURE_STORAGE_STORE_CONTAINER:smarketing-store-images}
|
||||||
|
max-file-size: ${AZURE_STORAGE_MAX_FILE_SIZE:10485760} # 10MB
|
||||||
management:
|
management:
|
||||||
endpoints:
|
endpoints:
|
||||||
web:
|
web:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user