mirror of
https://github.com/ktds-dg0501/kt-event-marketing.git
synced 2026-06-12 23:19:10 +00:00
content-service 핵심 비즈니스 로직 및 영속성 계층 구현 완료
- Domain 모델 구현 (ImageStyle, Platform, GeneratedImage, Content, Job) - JPA Entity 및 Repository 구현 (3개 엔티티, 3개 리포지토리) - UseCase 인터페이스 정의 (Inbound 6개, Outbound 8개) - Service 구현 (JobManagement, GetEventContent, GetImageList, GetImageDetail) - DTO 구현 (ContentCommand, ContentInfo, ImageInfo, JobInfo) - Application 설정 (ContentApplication, application.yml) - 컴파일 오류 수정 및 검증 완료 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
package com.kt.event.content.biz.domain;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 콘텐츠 도메인 모델
|
||||
* 이벤트에 대한 전체 콘텐츠 정보 (이미지 목록 포함)
|
||||
*/
|
||||
@Getter
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
public class Content {
|
||||
|
||||
/**
|
||||
* 콘텐츠 ID
|
||||
*/
|
||||
private final Long id;
|
||||
|
||||
/**
|
||||
* 이벤트 ID (이벤트 초안 ID)
|
||||
*/
|
||||
private final Long eventDraftId;
|
||||
|
||||
/**
|
||||
* 이벤트 제목
|
||||
*/
|
||||
private final String eventTitle;
|
||||
|
||||
/**
|
||||
* 이벤트 설명
|
||||
*/
|
||||
private final String eventDescription;
|
||||
|
||||
/**
|
||||
* 생성된 이미지 목록
|
||||
*/
|
||||
@Builder.Default
|
||||
private final List<GeneratedImage> images = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 생성일시
|
||||
*/
|
||||
private final LocalDateTime createdAt;
|
||||
|
||||
/**
|
||||
* 수정일시
|
||||
*/
|
||||
private final LocalDateTime updatedAt;
|
||||
|
||||
/**
|
||||
* 이미지 추가
|
||||
*
|
||||
* @param image 생성된 이미지
|
||||
*/
|
||||
public void addImage(GeneratedImage image) {
|
||||
this.images.add(image);
|
||||
}
|
||||
|
||||
/**
|
||||
* 선택된 이미지 조회
|
||||
*
|
||||
* @return 선택된 이미지 목록
|
||||
*/
|
||||
public List<GeneratedImage> getSelectedImages() {
|
||||
return images.stream()
|
||||
.filter(GeneratedImage::isSelected)
|
||||
.toList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 특정 스타일의 이미지 조회
|
||||
*
|
||||
* @param style 이미지 스타일
|
||||
* @return 해당 스타일의 이미지 목록
|
||||
*/
|
||||
public List<GeneratedImage> getImagesByStyle(ImageStyle style) {
|
||||
return images.stream()
|
||||
.filter(image -> image.getStyle() == style)
|
||||
.toList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 특정 플랫폼의 이미지 조회
|
||||
*
|
||||
* @param platform 플랫폼
|
||||
* @return 해당 플랫폼의 이미지 목록
|
||||
*/
|
||||
public List<GeneratedImage> getImagesByPlatform(Platform platform) {
|
||||
return images.stream()
|
||||
.filter(image -> image.getPlatform() == platform)
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package com.kt.event.content.biz.domain;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 생성된 이미지 도메인 모델
|
||||
* AI가 생성한 이미지의 비즈니스 정보
|
||||
*/
|
||||
@Getter
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
public class GeneratedImage {
|
||||
|
||||
/**
|
||||
* 이미지 ID
|
||||
*/
|
||||
private final Long id;
|
||||
|
||||
/**
|
||||
* 이벤트 ID (이벤트 초안 ID)
|
||||
*/
|
||||
private final Long eventDraftId;
|
||||
|
||||
/**
|
||||
* 이미지 스타일
|
||||
*/
|
||||
private final ImageStyle style;
|
||||
|
||||
/**
|
||||
* 플랫폼
|
||||
*/
|
||||
private final Platform platform;
|
||||
|
||||
/**
|
||||
* CDN URL (Azure Blob Storage)
|
||||
*/
|
||||
private final String cdnUrl;
|
||||
|
||||
/**
|
||||
* 프롬프트
|
||||
*/
|
||||
private final String prompt;
|
||||
|
||||
/**
|
||||
* 선택 여부
|
||||
*/
|
||||
private boolean selected;
|
||||
|
||||
/**
|
||||
* 생성일시
|
||||
*/
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
/**
|
||||
* 수정일시
|
||||
*/
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
/**
|
||||
* 이미지 선택
|
||||
*/
|
||||
public void select() {
|
||||
this.selected = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 이미지 선택 해제
|
||||
*/
|
||||
public void deselect() {
|
||||
this.selected = false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.kt.event.content.biz.domain;
|
||||
|
||||
/**
|
||||
* 이미지 스타일 enum
|
||||
* AI가 생성하는 이미지의 스타일 유형
|
||||
*/
|
||||
public enum ImageStyle {
|
||||
/**
|
||||
* 심플 스타일 - 깔끔하고 미니멀한 디자인
|
||||
*/
|
||||
SIMPLE("심플"),
|
||||
|
||||
/**
|
||||
* 화려한 스타일 - 화려하고 풍부한 디자인
|
||||
*/
|
||||
FANCY("화려한"),
|
||||
|
||||
/**
|
||||
* 트렌디 스타일 - 최신 트렌드를 반영한 디자인
|
||||
*/
|
||||
TRENDY("트렌디");
|
||||
|
||||
private final String displayName;
|
||||
|
||||
ImageStyle(String displayName) {
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
package com.kt.event.content.biz.domain;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* Job 도메인 모델
|
||||
* 이미지 생성 작업의 비즈니스 정보
|
||||
*/
|
||||
@Getter
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
public class Job {
|
||||
|
||||
/**
|
||||
* Job 상태 enum
|
||||
*/
|
||||
public enum Status {
|
||||
PENDING, // 대기 중
|
||||
PROCESSING, // 처리 중
|
||||
COMPLETED, // 완료
|
||||
FAILED // 실패
|
||||
}
|
||||
|
||||
/**
|
||||
* Job ID
|
||||
*/
|
||||
private final String id;
|
||||
|
||||
/**
|
||||
* 이벤트 ID (이벤트 초안 ID)
|
||||
*/
|
||||
private final Long eventDraftId;
|
||||
|
||||
/**
|
||||
* Job 타입 (image-generation)
|
||||
*/
|
||||
private final String jobType;
|
||||
|
||||
/**
|
||||
* Job 상태
|
||||
*/
|
||||
private Status status;
|
||||
|
||||
/**
|
||||
* 진행률 (0-100)
|
||||
*/
|
||||
private int progress;
|
||||
|
||||
/**
|
||||
* 결과 메시지
|
||||
*/
|
||||
private String resultMessage;
|
||||
|
||||
/**
|
||||
* 에러 메시지
|
||||
*/
|
||||
private String errorMessage;
|
||||
|
||||
/**
|
||||
* 생성일시
|
||||
*/
|
||||
private final LocalDateTime createdAt;
|
||||
|
||||
/**
|
||||
* 수정일시
|
||||
*/
|
||||
private final LocalDateTime updatedAt;
|
||||
|
||||
/**
|
||||
* Job 시작
|
||||
*/
|
||||
public void start() {
|
||||
this.status = Status.PROCESSING;
|
||||
this.progress = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 진행률 업데이트
|
||||
*
|
||||
* @param progress 진행률 (0-100)
|
||||
*/
|
||||
public void updateProgress(int progress) {
|
||||
if (progress < 0 || progress > 100) {
|
||||
throw new IllegalArgumentException("진행률은 0-100 사이여야 합니다");
|
||||
}
|
||||
this.progress = progress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Job 완료 처리
|
||||
*
|
||||
* @param resultMessage 결과 메시지
|
||||
*/
|
||||
public void complete(String resultMessage) {
|
||||
this.status = Status.COMPLETED;
|
||||
this.progress = 100;
|
||||
this.resultMessage = resultMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Job 실패 처리
|
||||
*
|
||||
* @param errorMessage 에러 메시지
|
||||
*/
|
||||
public void fail(String errorMessage) {
|
||||
this.status = Status.FAILED;
|
||||
this.errorMessage = errorMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Job 진행 중 여부
|
||||
*
|
||||
* @return 진행 중이면 true
|
||||
*/
|
||||
public boolean isProcessing() {
|
||||
return status == Status.PROCESSING;
|
||||
}
|
||||
|
||||
/**
|
||||
* Job 완료 여부
|
||||
*
|
||||
* @return 완료되었으면 true
|
||||
*/
|
||||
public boolean isCompleted() {
|
||||
return status == Status.COMPLETED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Job 실패 여부
|
||||
*
|
||||
* @return 실패했으면 true
|
||||
*/
|
||||
public boolean isFailed() {
|
||||
return status == Status.FAILED;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.kt.event.content.biz.domain;
|
||||
|
||||
/**
|
||||
* 플랫폼 enum
|
||||
* 이미지가 배포될 SNS 플랫폼 유형
|
||||
*/
|
||||
public enum Platform {
|
||||
/**
|
||||
* Instagram - 1080x1080 정사각형
|
||||
*/
|
||||
INSTAGRAM("Instagram", 1080, 1080),
|
||||
|
||||
/**
|
||||
* 네이버 블로그 - 800x600
|
||||
*/
|
||||
NAVER("네이버 블로그", 800, 600),
|
||||
|
||||
/**
|
||||
* 카카오 채널 - 800x800 정사각형
|
||||
*/
|
||||
KAKAO("카카오 채널", 800, 800);
|
||||
|
||||
private final String displayName;
|
||||
private final int width;
|
||||
private final int height;
|
||||
|
||||
Platform(String displayName, int width, int height) {
|
||||
this.displayName = displayName;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
public int getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
/**
|
||||
* 이미지 크기 문자열 반환
|
||||
*
|
||||
* @return 가로x세로 형식 (예: 1080x1080)
|
||||
*/
|
||||
public String getSizeString() {
|
||||
return width + "x" + height;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.kt.event.content.biz.dto;
|
||||
|
||||
import com.kt.event.content.biz.domain.ImageStyle;
|
||||
import com.kt.event.content.biz.domain.Platform;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 콘텐츠 관련 커맨드 DTO
|
||||
*/
|
||||
public class ContentCommand {
|
||||
|
||||
/**
|
||||
* 이미지 생성 요청 커맨드
|
||||
*/
|
||||
@Getter
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
public static class GenerateImages {
|
||||
private Long eventDraftId;
|
||||
private String eventTitle;
|
||||
private String eventDescription;
|
||||
private List<ImageStyle> styles;
|
||||
private List<Platform> platforms;
|
||||
}
|
||||
|
||||
/**
|
||||
* 이미지 재생성 요청 커맨드
|
||||
*/
|
||||
@Getter
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
public static class RegenerateImage {
|
||||
private Long imageId;
|
||||
private String newPrompt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.kt.event.content.biz.dto;
|
||||
|
||||
import com.kt.event.content.biz.domain.Content;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 콘텐츠 정보 DTO
|
||||
*/
|
||||
@Getter
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
public class ContentInfo {
|
||||
|
||||
private Long id;
|
||||
private Long eventDraftId;
|
||||
private String eventTitle;
|
||||
private String eventDescription;
|
||||
private List<ImageInfo> images;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
/**
|
||||
* 도메인 모델로부터 생성
|
||||
*
|
||||
* @param content 콘텐츠 도메인 모델
|
||||
* @return ContentInfo
|
||||
*/
|
||||
public static ContentInfo from(Content content) {
|
||||
return ContentInfo.builder()
|
||||
.id(content.getId())
|
||||
.eventDraftId(content.getEventDraftId())
|
||||
.eventTitle(content.getEventTitle())
|
||||
.eventDescription(content.getEventDescription())
|
||||
.images(content.getImages().stream()
|
||||
.map(ImageInfo::from)
|
||||
.collect(Collectors.toList()))
|
||||
.createdAt(content.getCreatedAt())
|
||||
.updatedAt(content.getUpdatedAt())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.kt.event.content.biz.dto;
|
||||
|
||||
import com.kt.event.content.biz.domain.GeneratedImage;
|
||||
import com.kt.event.content.biz.domain.ImageStyle;
|
||||
import com.kt.event.content.biz.domain.Platform;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 이미지 정보 DTO
|
||||
*/
|
||||
@Getter
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
public class ImageInfo {
|
||||
|
||||
private Long id;
|
||||
private Long eventDraftId;
|
||||
private ImageStyle style;
|
||||
private Platform platform;
|
||||
private String cdnUrl;
|
||||
private String prompt;
|
||||
private boolean selected;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
/**
|
||||
* 도메인 모델로부터 생성
|
||||
*
|
||||
* @param image 이미지 도메인 모델
|
||||
* @return ImageInfo
|
||||
*/
|
||||
public static ImageInfo from(GeneratedImage image) {
|
||||
return ImageInfo.builder()
|
||||
.id(image.getId())
|
||||
.eventDraftId(image.getEventDraftId())
|
||||
.style(image.getStyle())
|
||||
.platform(image.getPlatform())
|
||||
.cdnUrl(image.getCdnUrl())
|
||||
.prompt(image.getPrompt())
|
||||
.selected(image.isSelected())
|
||||
.createdAt(image.getCreatedAt())
|
||||
.updatedAt(image.getUpdatedAt())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.kt.event.content.biz.dto;
|
||||
|
||||
import com.kt.event.content.biz.domain.Job;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* Job 정보 DTO
|
||||
*/
|
||||
@Getter
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
public class JobInfo {
|
||||
|
||||
private String id;
|
||||
private Long eventDraftId;
|
||||
private String jobType;
|
||||
private Job.Status status;
|
||||
private int progress;
|
||||
private String resultMessage;
|
||||
private String errorMessage;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
/**
|
||||
* 도메인 모델로부터 생성
|
||||
*
|
||||
* @param job Job 도메인 모델
|
||||
* @return JobInfo
|
||||
*/
|
||||
public static JobInfo from(Job job) {
|
||||
return JobInfo.builder()
|
||||
.id(job.getId())
|
||||
.eventDraftId(job.getEventDraftId())
|
||||
.jobType(job.getJobType())
|
||||
.status(job.getStatus())
|
||||
.progress(job.getProgress())
|
||||
.resultMessage(job.getResultMessage())
|
||||
.errorMessage(job.getErrorMessage())
|
||||
.createdAt(job.getCreatedAt())
|
||||
.updatedAt(job.getUpdatedAt())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package com.kt.event.content.biz.service;
|
||||
|
||||
import com.kt.event.common.exception.BusinessException;
|
||||
import com.kt.event.common.exception.ErrorCode;
|
||||
import com.kt.event.content.biz.domain.Content;
|
||||
import com.kt.event.content.biz.dto.ContentInfo;
|
||||
import com.kt.event.content.biz.usecase.in.GetEventContentUseCase;
|
||||
import com.kt.event.content.biz.usecase.out.ContentReader;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* 이벤트 콘텐츠 조회 서비스
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Transactional(readOnly = true)
|
||||
public class GetEventContentService implements GetEventContentUseCase {
|
||||
|
||||
private final ContentReader contentReader;
|
||||
|
||||
@Override
|
||||
public ContentInfo execute(Long eventDraftId) {
|
||||
Content content = contentReader.findByEventDraftIdWithImages(eventDraftId)
|
||||
.orElseThrow(() -> new BusinessException(ErrorCode.COMMON_001, "콘텐츠를 찾을 수 없습니다"));
|
||||
|
||||
return ContentInfo.from(content);
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package com.kt.event.content.biz.service;
|
||||
|
||||
import com.kt.event.common.exception.BusinessException;
|
||||
import com.kt.event.common.exception.ErrorCode;
|
||||
import com.kt.event.content.biz.domain.GeneratedImage;
|
||||
import com.kt.event.content.biz.dto.ImageInfo;
|
||||
import com.kt.event.content.biz.usecase.in.GetImageDetailUseCase;
|
||||
import com.kt.event.content.biz.usecase.out.ContentReader;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* 이미지 상세 조회 서비스
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Transactional(readOnly = true)
|
||||
public class GetImageDetailService implements GetImageDetailUseCase {
|
||||
|
||||
private final ContentReader contentReader;
|
||||
|
||||
@Override
|
||||
public ImageInfo execute(Long imageId) {
|
||||
GeneratedImage image = contentReader.findImageById(imageId)
|
||||
.orElseThrow(() -> new BusinessException(ErrorCode.COMMON_001, "이미지를 찾을 수 없습니다"));
|
||||
|
||||
return ImageInfo.from(image);
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package com.kt.event.content.biz.service;
|
||||
|
||||
import com.kt.event.content.biz.domain.GeneratedImage;
|
||||
import com.kt.event.content.biz.dto.ImageInfo;
|
||||
import com.kt.event.content.biz.usecase.in.GetImageListUseCase;
|
||||
import com.kt.event.content.biz.usecase.out.ContentReader;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 이미지 목록 조회 서비스
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Transactional(readOnly = true)
|
||||
public class GetImageListService implements GetImageListUseCase {
|
||||
|
||||
private final ContentReader contentReader;
|
||||
|
||||
@Override
|
||||
public List<ImageInfo> execute(Long eventDraftId) {
|
||||
List<GeneratedImage> images = contentReader.findImagesByEventDraftId(eventDraftId);
|
||||
return images.stream()
|
||||
.map(ImageInfo::from)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package com.kt.event.content.biz.service;
|
||||
|
||||
import com.kt.event.common.exception.BusinessException;
|
||||
import com.kt.event.common.exception.ErrorCode;
|
||||
import com.kt.event.content.biz.domain.Job;
|
||||
import com.kt.event.content.biz.dto.JobInfo;
|
||||
import com.kt.event.content.biz.usecase.in.GetJobStatusUseCase;
|
||||
import com.kt.event.content.biz.usecase.out.JobReader;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* Job 관리 서비스
|
||||
* Job 상태 조회 기능 제공
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Transactional(readOnly = true)
|
||||
public class JobManagementService implements GetJobStatusUseCase {
|
||||
|
||||
private final JobReader jobReader;
|
||||
|
||||
@Override
|
||||
public JobInfo execute(String jobId) {
|
||||
Job job = jobReader.findById(jobId)
|
||||
.orElseThrow(() -> new BusinessException(ErrorCode.COMMON_001, "Job을 찾을 수 없습니다"));
|
||||
|
||||
return JobInfo.from(job);
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package com.kt.event.content.biz.usecase.in;
|
||||
|
||||
import com.kt.event.content.biz.dto.ContentCommand;
|
||||
import com.kt.event.content.biz.dto.JobInfo;
|
||||
|
||||
/**
|
||||
* 이미지 생성 UseCase
|
||||
* 비동기로 이미지 생성 작업을 시작
|
||||
*/
|
||||
public interface GenerateImagesUseCase {
|
||||
|
||||
/**
|
||||
* 이미지 생성 요청
|
||||
*
|
||||
* @param command 이미지 생성 커맨드
|
||||
* @return Job 정보
|
||||
*/
|
||||
JobInfo execute(ContentCommand.GenerateImages command);
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package com.kt.event.content.biz.usecase.in;
|
||||
|
||||
import com.kt.event.content.biz.dto.ContentInfo;
|
||||
|
||||
/**
|
||||
* 이벤트 콘텐츠 조회 UseCase
|
||||
*/
|
||||
public interface GetEventContentUseCase {
|
||||
|
||||
/**
|
||||
* 이벤트 전체 콘텐츠 조회 (이미지 목록 포함)
|
||||
*
|
||||
* @param eventDraftId 이벤트 초안 ID
|
||||
* @return 콘텐츠 정보
|
||||
*/
|
||||
ContentInfo execute(Long eventDraftId);
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package com.kt.event.content.biz.usecase.in;
|
||||
|
||||
import com.kt.event.content.biz.dto.ImageInfo;
|
||||
|
||||
/**
|
||||
* 이미지 상세 조회 UseCase
|
||||
*/
|
||||
public interface GetImageDetailUseCase {
|
||||
|
||||
/**
|
||||
* 이미지 상세 정보 조회
|
||||
*
|
||||
* @param imageId 이미지 ID
|
||||
* @return 이미지 정보
|
||||
*/
|
||||
ImageInfo execute(Long imageId);
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package com.kt.event.content.biz.usecase.in;
|
||||
|
||||
import com.kt.event.content.biz.dto.ImageInfo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 이미지 목록 조회 UseCase
|
||||
*/
|
||||
public interface GetImageListUseCase {
|
||||
|
||||
/**
|
||||
* 이벤트의 이미지 목록 조회
|
||||
*
|
||||
* @param eventDraftId 이벤트 초안 ID
|
||||
* @return 이미지 정보 목록
|
||||
*/
|
||||
List<ImageInfo> execute(Long eventDraftId);
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package com.kt.event.content.biz.usecase.in;
|
||||
|
||||
import com.kt.event.content.biz.dto.JobInfo;
|
||||
|
||||
/**
|
||||
* Job 상태 조회 UseCase
|
||||
*/
|
||||
public interface GetJobStatusUseCase {
|
||||
|
||||
/**
|
||||
* Job 상태 조회
|
||||
*
|
||||
* @param jobId Job ID
|
||||
* @return Job 정보
|
||||
*/
|
||||
JobInfo execute(String jobId);
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package com.kt.event.content.biz.usecase.in;
|
||||
|
||||
import com.kt.event.content.biz.dto.ContentCommand;
|
||||
import com.kt.event.content.biz.dto.JobInfo;
|
||||
|
||||
/**
|
||||
* 이미지 재생성 UseCase
|
||||
*/
|
||||
public interface RegenerateImageUseCase {
|
||||
|
||||
/**
|
||||
* 이미지 재생성 요청
|
||||
*
|
||||
* @param command 이미지 재생성 커맨드
|
||||
* @return Job 정보
|
||||
*/
|
||||
JobInfo execute(ContentCommand.RegenerateImage command);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.kt.event.content.biz.usecase.out;
|
||||
|
||||
/**
|
||||
* CDN 업로드 포트
|
||||
* Azure Blob Storage에 이미지 업로드
|
||||
*/
|
||||
public interface CDNUploader {
|
||||
|
||||
/**
|
||||
* 이미지 업로드
|
||||
*
|
||||
* @param imageData 이미지 바이트 데이터
|
||||
* @param fileName 파일명
|
||||
* @return CDN URL
|
||||
*/
|
||||
String upload(byte[] imageData, String fileName);
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.kt.event.content.biz.usecase.out;
|
||||
|
||||
import com.kt.event.content.biz.domain.Content;
|
||||
import com.kt.event.content.biz.domain.GeneratedImage;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 콘텐츠 조회 포트
|
||||
*/
|
||||
public interface ContentReader {
|
||||
|
||||
/**
|
||||
* 이벤트 초안 ID로 콘텐츠 조회 (이미지 목록 포함)
|
||||
*
|
||||
* @param eventDraftId 이벤트 초안 ID
|
||||
* @return 콘텐츠 도메인 모델
|
||||
*/
|
||||
Optional<Content> findByEventDraftIdWithImages(Long eventDraftId);
|
||||
|
||||
/**
|
||||
* 이미지 ID로 이미지 조회
|
||||
*
|
||||
* @param imageId 이미지 ID
|
||||
* @return 이미지 도메인 모델
|
||||
*/
|
||||
Optional<GeneratedImage> findImageById(Long imageId);
|
||||
|
||||
/**
|
||||
* 이벤트 초안 ID로 이미지 목록 조회
|
||||
*
|
||||
* @param eventDraftId 이벤트 초안 ID
|
||||
* @return 이미지 도메인 모델 목록
|
||||
*/
|
||||
List<GeneratedImage> findImagesByEventDraftId(Long eventDraftId);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.kt.event.content.biz.usecase.out;
|
||||
|
||||
import com.kt.event.content.biz.domain.Content;
|
||||
import com.kt.event.content.biz.domain.GeneratedImage;
|
||||
|
||||
/**
|
||||
* 콘텐츠 저장 포트
|
||||
*/
|
||||
public interface ContentWriter {
|
||||
|
||||
/**
|
||||
* 콘텐츠 저장
|
||||
*
|
||||
* @param content 콘텐츠 도메인 모델
|
||||
* @return 저장된 콘텐츠
|
||||
*/
|
||||
Content save(Content content);
|
||||
|
||||
/**
|
||||
* 이미지 저장
|
||||
*
|
||||
* @param image 이미지 도메인 모델
|
||||
* @return 저장된 이미지
|
||||
*/
|
||||
GeneratedImage saveImage(GeneratedImage image);
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package com.kt.event.content.biz.usecase.out;
|
||||
|
||||
import com.kt.event.content.biz.domain.ImageStyle;
|
||||
import com.kt.event.content.biz.domain.Platform;
|
||||
|
||||
/**
|
||||
* 이미지 생성 API 호출 포트
|
||||
* Stable Diffusion, DALL-E 등 외부 이미지 생성 API 호출
|
||||
*/
|
||||
public interface ImageGeneratorCaller {
|
||||
|
||||
/**
|
||||
* 이미지 생성
|
||||
*
|
||||
* @param prompt 프롬프트
|
||||
* @param style 이미지 스타일
|
||||
* @param platform 플랫폼 (이미지 크기 결정)
|
||||
* @return 생성된 이미지 바이트 데이터
|
||||
*/
|
||||
byte[] generateImage(String prompt, ImageStyle style, Platform platform);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.kt.event.content.biz.usecase.out;
|
||||
|
||||
import com.kt.event.content.biz.domain.Job;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Job 조회 포트
|
||||
*/
|
||||
public interface JobReader {
|
||||
|
||||
/**
|
||||
* Job ID로 조회
|
||||
*
|
||||
* @param jobId Job ID
|
||||
* @return Job 도메인 모델
|
||||
*/
|
||||
Optional<Job> findById(String jobId);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.kt.event.content.biz.usecase.out;
|
||||
|
||||
import com.kt.event.content.biz.domain.Job;
|
||||
|
||||
/**
|
||||
* Job 저장 포트
|
||||
*/
|
||||
public interface JobWriter {
|
||||
|
||||
/**
|
||||
* Job 저장
|
||||
*
|
||||
* @param job Job 도메인 모델
|
||||
* @return 저장된 Job
|
||||
*/
|
||||
Job save(Job job);
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package com.kt.event.content.biz.usecase.out;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Redis AI 데이터 조회 포트
|
||||
* Event Service가 저장한 AI 추천 데이터를 읽음
|
||||
*/
|
||||
public interface RedisAIDataReader {
|
||||
|
||||
/**
|
||||
* AI 추천 데이터 조회
|
||||
*
|
||||
* @param eventDraftId 이벤트 초안 ID
|
||||
* @return AI 추천 데이터 (JSON 형태의 Map)
|
||||
*/
|
||||
Optional<Map<String, Object>> getAIRecommendation(Long eventDraftId);
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package com.kt.event.content.biz.usecase.out;
|
||||
|
||||
import com.kt.event.content.biz.domain.GeneratedImage;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Redis 이미지 데이터 저장 포트
|
||||
* 생성된 이미지 정보를 Redis에 캐싱
|
||||
*/
|
||||
public interface RedisImageWriter {
|
||||
|
||||
/**
|
||||
* 이미지 목록 캐싱
|
||||
*
|
||||
* @param eventDraftId 이벤트 초안 ID
|
||||
* @param images 이미지 목록
|
||||
* @param ttlSeconds TTL (초)
|
||||
*/
|
||||
void cacheImages(Long eventDraftId, List<GeneratedImage> images, long ttlSeconds);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.kt.event.content.infra;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.domain.EntityScan;
|
||||
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
|
||||
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||
|
||||
/**
|
||||
* Content Service Application
|
||||
*/
|
||||
@SpringBootApplication(scanBasePackages = {
|
||||
"com.kt.event.content",
|
||||
"com.kt.event.common"
|
||||
})
|
||||
@EntityScan(basePackages = {
|
||||
"com.kt.event.content.infra.gateway.entity",
|
||||
"com.kt.event.common.entity"
|
||||
})
|
||||
@EnableJpaRepositories(basePackages = "com.kt.event.content.infra.gateway.repository")
|
||||
@EnableJpaAuditing
|
||||
public class ContentApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ContentApplication.class, args);
|
||||
}
|
||||
}
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
package com.kt.event.content.infra.gateway.entity;
|
||||
|
||||
import com.kt.event.common.entity.BaseTimeEntity;
|
||||
import com.kt.event.content.biz.domain.Content;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 콘텐츠 엔티티
|
||||
* 이벤트에 대한 전체 콘텐츠 정보를 저장
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "contents")
|
||||
@Getter
|
||||
@NoArgsConstructor(access = AccessLevel.PROTECTED)
|
||||
@AllArgsConstructor
|
||||
public class ContentEntity extends BaseTimeEntity {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 이벤트 ID (이벤트 초안 ID)
|
||||
*/
|
||||
@Column(name = "event_draft_id", nullable = false)
|
||||
private Long eventDraftId;
|
||||
|
||||
/**
|
||||
* 이벤트 제목
|
||||
*/
|
||||
@Column(name = "event_title", nullable = false, length = 200)
|
||||
private String eventTitle;
|
||||
|
||||
/**
|
||||
* 이벤트 설명
|
||||
*/
|
||||
@Column(name = "event_description", columnDefinition = "TEXT")
|
||||
private String eventDescription;
|
||||
|
||||
/**
|
||||
* 생성된 이미지 목록
|
||||
*/
|
||||
@OneToMany(mappedBy = "content", cascade = CascadeType.ALL, orphanRemoval = true)
|
||||
private List<GeneratedImageEntity> images = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 정적 팩토리 메서드: 새 콘텐츠 생성
|
||||
*
|
||||
* @param eventDraftId 이벤트 초안 ID
|
||||
* @param eventTitle 이벤트 제목
|
||||
* @param eventDescription 이벤트 설명
|
||||
* @return ContentEntity
|
||||
*/
|
||||
public static ContentEntity create(Long eventDraftId, String eventTitle, String eventDescription) {
|
||||
ContentEntity entity = new ContentEntity();
|
||||
entity.eventDraftId = eventDraftId;
|
||||
entity.eventTitle = eventTitle;
|
||||
entity.eventDescription = eventDescription;
|
||||
return entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* 도메인 모델로 변환
|
||||
*
|
||||
* @return Content 도메인 모델
|
||||
*/
|
||||
public Content toDomain() {
|
||||
return Content.builder()
|
||||
.id(id)
|
||||
.eventDraftId(eventDraftId)
|
||||
.eventTitle(eventTitle)
|
||||
.eventDescription(eventDescription)
|
||||
.images(images.stream()
|
||||
.map(GeneratedImageEntity::toDomain)
|
||||
.collect(Collectors.toList()))
|
||||
.createdAt(getCreatedAt())
|
||||
.updatedAt(getUpdatedAt())
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 이미지 추가
|
||||
*
|
||||
* @param image 생성된 이미지 엔티티
|
||||
*/
|
||||
public void addImage(GeneratedImageEntity image) {
|
||||
images.add(image);
|
||||
image.assignContent(this);
|
||||
}
|
||||
}
|
||||
+139
@@ -0,0 +1,139 @@
|
||||
package com.kt.event.content.infra.gateway.entity;
|
||||
|
||||
import com.kt.event.common.entity.BaseTimeEntity;
|
||||
import com.kt.event.content.biz.domain.GeneratedImage;
|
||||
import com.kt.event.content.biz.domain.ImageStyle;
|
||||
import com.kt.event.content.biz.domain.Platform;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 생성된 이미지 엔티티
|
||||
* AI가 생성한 이미지 정보를 저장
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "generated_images", indexes = {
|
||||
@Index(name = "idx_event_draft_id", columnList = "event_draft_id"),
|
||||
@Index(name = "idx_style_platform", columnList = "style,platform")
|
||||
})
|
||||
@Getter
|
||||
@NoArgsConstructor(access = AccessLevel.PROTECTED)
|
||||
@AllArgsConstructor
|
||||
public class GeneratedImageEntity extends BaseTimeEntity {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 콘텐츠 (양방향 관계)
|
||||
*/
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "content_id")
|
||||
private ContentEntity content;
|
||||
|
||||
/**
|
||||
* 이벤트 ID (이벤트 초안 ID)
|
||||
*/
|
||||
@Column(name = "event_draft_id", nullable = false)
|
||||
private Long eventDraftId;
|
||||
|
||||
/**
|
||||
* 이미지 스타일
|
||||
*/
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "style", nullable = false, length = 20)
|
||||
private ImageStyle style;
|
||||
|
||||
/**
|
||||
* 플랫폼
|
||||
*/
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "platform", nullable = false, length = 20)
|
||||
private Platform platform;
|
||||
|
||||
/**
|
||||
* CDN URL (Azure Blob Storage)
|
||||
*/
|
||||
@Column(name = "cdn_url", nullable = false, length = 500)
|
||||
private String cdnUrl;
|
||||
|
||||
/**
|
||||
* 프롬프트
|
||||
*/
|
||||
@Column(name = "prompt", columnDefinition = "TEXT")
|
||||
private String prompt;
|
||||
|
||||
/**
|
||||
* 선택 여부
|
||||
*/
|
||||
@Column(name = "selected", nullable = false)
|
||||
private boolean selected;
|
||||
|
||||
/**
|
||||
* 정적 팩토리 메서드: 새 이미지 생성
|
||||
*
|
||||
* @param eventDraftId 이벤트 초안 ID
|
||||
* @param style 이미지 스타일
|
||||
* @param platform 플랫폼
|
||||
* @param cdnUrl CDN URL
|
||||
* @param prompt 프롬프트
|
||||
* @return GeneratedImageEntity
|
||||
*/
|
||||
public static GeneratedImageEntity create(Long eventDraftId, ImageStyle style, Platform platform,
|
||||
String cdnUrl, String prompt) {
|
||||
GeneratedImageEntity entity = new GeneratedImageEntity();
|
||||
entity.eventDraftId = eventDraftId;
|
||||
entity.style = style;
|
||||
entity.platform = platform;
|
||||
entity.cdnUrl = cdnUrl;
|
||||
entity.prompt = prompt;
|
||||
entity.selected = false;
|
||||
return entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* 도메인 모델로 변환
|
||||
*
|
||||
* @return GeneratedImage 도메인 모델
|
||||
*/
|
||||
public GeneratedImage toDomain() {
|
||||
return GeneratedImage.builder()
|
||||
.id(id)
|
||||
.eventDraftId(eventDraftId)
|
||||
.style(style)
|
||||
.platform(platform)
|
||||
.cdnUrl(cdnUrl)
|
||||
.prompt(prompt)
|
||||
.selected(selected)
|
||||
.createdAt(getCreatedAt())
|
||||
.updatedAt(getUpdatedAt())
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 콘텐츠 할당 (양방향 관계 설정용)
|
||||
*
|
||||
* @param content 콘텐츠 엔티티
|
||||
*/
|
||||
protected void assignContent(ContentEntity content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
/**
|
||||
* 이미지 선택
|
||||
*/
|
||||
public void select() {
|
||||
this.selected = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 이미지 선택 해제
|
||||
*/
|
||||
public void deselect() {
|
||||
this.selected = false;
|
||||
}
|
||||
}
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
package com.kt.event.content.infra.gateway.entity;
|
||||
|
||||
import com.kt.event.common.entity.BaseTimeEntity;
|
||||
import com.kt.event.content.biz.domain.Job;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Job 엔티티
|
||||
* 이미지 생성 작업 정보를 저장
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "jobs", indexes = {
|
||||
@Index(name = "idx_event_draft_id", columnList = "event_draft_id"),
|
||||
@Index(name = "idx_status", columnList = "status")
|
||||
})
|
||||
@Getter
|
||||
@NoArgsConstructor(access = AccessLevel.PROTECTED)
|
||||
@AllArgsConstructor
|
||||
public class JobEntity extends BaseTimeEntity {
|
||||
|
||||
@Id
|
||||
@Column(name = "id", length = 36)
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 이벤트 ID (이벤트 초안 ID)
|
||||
*/
|
||||
@Column(name = "event_draft_id", nullable = false)
|
||||
private Long eventDraftId;
|
||||
|
||||
/**
|
||||
* Job 타입
|
||||
*/
|
||||
@Column(name = "job_type", nullable = false, length = 50)
|
||||
private String jobType;
|
||||
|
||||
/**
|
||||
* Job 상태
|
||||
*/
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "status", nullable = false, length = 20)
|
||||
private Job.Status status;
|
||||
|
||||
/**
|
||||
* 진행률 (0-100)
|
||||
*/
|
||||
@Column(name = "progress", nullable = false)
|
||||
private int progress;
|
||||
|
||||
/**
|
||||
* 결과 메시지
|
||||
*/
|
||||
@Column(name = "result_message", columnDefinition = "TEXT")
|
||||
private String resultMessage;
|
||||
|
||||
/**
|
||||
* 에러 메시지
|
||||
*/
|
||||
@Column(name = "error_message", columnDefinition = "TEXT")
|
||||
private String errorMessage;
|
||||
|
||||
/**
|
||||
* 정적 팩토리 메서드: 새 Job 생성
|
||||
*
|
||||
* @param id Job ID (UUID)
|
||||
* @param eventDraftId 이벤트 초안 ID
|
||||
* @param jobType Job 타입
|
||||
* @return JobEntity
|
||||
*/
|
||||
public static JobEntity create(String id, Long eventDraftId, String jobType) {
|
||||
JobEntity entity = new JobEntity();
|
||||
entity.id = id;
|
||||
entity.eventDraftId = eventDraftId;
|
||||
entity.jobType = jobType;
|
||||
entity.status = Job.Status.PENDING;
|
||||
entity.progress = 0;
|
||||
return entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* 도메인 모델로 변환
|
||||
*
|
||||
* @return Job 도메인 모델
|
||||
*/
|
||||
public Job toDomain() {
|
||||
return Job.builder()
|
||||
.id(id)
|
||||
.eventDraftId(eventDraftId)
|
||||
.jobType(jobType)
|
||||
.status(status)
|
||||
.progress(progress)
|
||||
.resultMessage(resultMessage)
|
||||
.errorMessage(errorMessage)
|
||||
.createdAt(getCreatedAt())
|
||||
.updatedAt(getUpdatedAt())
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Job 시작
|
||||
*/
|
||||
public void start() {
|
||||
this.status = Job.Status.PROCESSING;
|
||||
this.progress = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 진행률 업데이트
|
||||
*
|
||||
* @param progress 진행률 (0-100)
|
||||
*/
|
||||
public void updateProgress(int progress) {
|
||||
if (progress < 0 || progress > 100) {
|
||||
throw new IllegalArgumentException("진행률은 0-100 사이여야 합니다");
|
||||
}
|
||||
this.progress = progress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Job 완료 처리
|
||||
*
|
||||
* @param resultMessage 결과 메시지
|
||||
*/
|
||||
public void complete(String resultMessage) {
|
||||
this.status = Job.Status.COMPLETED;
|
||||
this.progress = 100;
|
||||
this.resultMessage = resultMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Job 실패 처리
|
||||
*
|
||||
* @param errorMessage 에러 메시지
|
||||
*/
|
||||
public void fail(String errorMessage) {
|
||||
this.status = Job.Status.FAILED;
|
||||
this.errorMessage = errorMessage;
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
package com.kt.event.content.infra.gateway.repository;
|
||||
|
||||
import com.kt.event.content.infra.gateway.entity.ContentEntity;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 콘텐츠 JPA 리포지토리
|
||||
*/
|
||||
public interface ContentJpaRepository extends JpaRepository<ContentEntity, Long> {
|
||||
|
||||
/**
|
||||
* 이벤트 초안 ID로 콘텐츠 조회 (이미지 목록 포함)
|
||||
*
|
||||
* @param eventDraftId 이벤트 초안 ID
|
||||
* @return 콘텐츠 엔티티
|
||||
*/
|
||||
@Query("SELECT DISTINCT c FROM ContentEntity c " +
|
||||
"LEFT JOIN FETCH c.images " +
|
||||
"WHERE c.eventDraftId = :eventDraftId")
|
||||
Optional<ContentEntity> findByEventDraftIdWithImages(@Param("eventDraftId") Long eventDraftId);
|
||||
|
||||
/**
|
||||
* 이벤트 초안 ID로 콘텐츠 조회
|
||||
*
|
||||
* @param eventDraftId 이벤트 초안 ID
|
||||
* @return 콘텐츠 엔티티
|
||||
*/
|
||||
Optional<ContentEntity> findByEventDraftId(Long eventDraftId);
|
||||
|
||||
/**
|
||||
* 이벤트 초안 ID로 콘텐츠 존재 여부 확인
|
||||
*
|
||||
* @param eventDraftId 이벤트 초안 ID
|
||||
* @return 존재 여부
|
||||
*/
|
||||
boolean existsByEventDraftId(Long eventDraftId);
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
package com.kt.event.content.infra.gateway.repository;
|
||||
|
||||
import com.kt.event.content.biz.domain.ImageStyle;
|
||||
import com.kt.event.content.biz.domain.Platform;
|
||||
import com.kt.event.content.infra.gateway.entity.GeneratedImageEntity;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 생성된 이미지 JPA 리포지토리
|
||||
*/
|
||||
public interface GeneratedImageJpaRepository extends JpaRepository<GeneratedImageEntity, Long> {
|
||||
|
||||
/**
|
||||
* 이벤트 초안 ID로 이미지 목록 조회
|
||||
*
|
||||
* @param eventDraftId 이벤트 초안 ID
|
||||
* @return 이미지 엔티티 목록
|
||||
*/
|
||||
List<GeneratedImageEntity> findByEventDraftId(Long eventDraftId);
|
||||
|
||||
/**
|
||||
* 이벤트 초안 ID와 스타일로 이미지 목록 조회
|
||||
*
|
||||
* @param eventDraftId 이벤트 초안 ID
|
||||
* @param style 이미지 스타일
|
||||
* @return 이미지 엔티티 목록
|
||||
*/
|
||||
List<GeneratedImageEntity> findByEventDraftIdAndStyle(Long eventDraftId, ImageStyle style);
|
||||
|
||||
/**
|
||||
* 이벤트 초안 ID와 플랫폼으로 이미지 목록 조회
|
||||
*
|
||||
* @param eventDraftId 이벤트 초안 ID
|
||||
* @param platform 플랫폼
|
||||
* @return 이미지 엔티티 목록
|
||||
*/
|
||||
List<GeneratedImageEntity> findByEventDraftIdAndPlatform(Long eventDraftId, Platform platform);
|
||||
|
||||
/**
|
||||
* 이벤트 초안 ID와 선택 여부로 이미지 목록 조회
|
||||
*
|
||||
* @param eventDraftId 이벤트 초안 ID
|
||||
* @param selected 선택 여부
|
||||
* @return 이미지 엔티티 목록
|
||||
*/
|
||||
List<GeneratedImageEntity> findByEventDraftIdAndSelected(Long eventDraftId, boolean selected);
|
||||
|
||||
/**
|
||||
* 이벤트 초안 ID로 선택된 이미지 목록 조회
|
||||
*
|
||||
* @param eventDraftId 이벤트 초안 ID
|
||||
* @return 선택된 이미지 엔티티 목록
|
||||
*/
|
||||
@Query("SELECT i FROM GeneratedImageEntity i WHERE i.eventDraftId = :eventDraftId AND i.selected = true")
|
||||
List<GeneratedImageEntity> findSelectedImages(@Param("eventDraftId") Long eventDraftId);
|
||||
|
||||
/**
|
||||
* 이벤트 초안 ID로 모든 이미지 선택 해제
|
||||
*
|
||||
* @param eventDraftId 이벤트 초안 ID
|
||||
*/
|
||||
@Query("UPDATE GeneratedImageEntity i SET i.selected = false WHERE i.eventDraftId = :eventDraftId")
|
||||
void deselectAllByEventDraftId(@Param("eventDraftId") Long eventDraftId);
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
package com.kt.event.content.infra.gateway.repository;
|
||||
|
||||
import com.kt.event.content.biz.domain.Job;
|
||||
import com.kt.event.content.infra.gateway.entity.JobEntity;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Job JPA 리포지토리
|
||||
*/
|
||||
public interface JobJpaRepository extends JpaRepository<JobEntity, String> {
|
||||
|
||||
/**
|
||||
* 이벤트 초안 ID로 Job 목록 조회
|
||||
*
|
||||
* @param eventDraftId 이벤트 초안 ID
|
||||
* @return Job 엔티티 목록
|
||||
*/
|
||||
List<JobEntity> findByEventDraftId(Long eventDraftId);
|
||||
|
||||
/**
|
||||
* 이벤트 초안 ID와 상태로 Job 목록 조회
|
||||
*
|
||||
* @param eventDraftId 이벤트 초안 ID
|
||||
* @param status Job 상태
|
||||
* @return Job 엔티티 목록
|
||||
*/
|
||||
List<JobEntity> findByEventDraftIdAndStatus(Long eventDraftId, Job.Status status);
|
||||
|
||||
/**
|
||||
* 이벤트 초안 ID와 Job 타입으로 최신 Job 조회
|
||||
*
|
||||
* @param eventDraftId 이벤트 초안 ID
|
||||
* @param jobType Job 타입
|
||||
* @return Job 엔티티
|
||||
*/
|
||||
Optional<JobEntity> findFirstByEventDraftIdAndJobTypeOrderByCreatedAtDesc(Long eventDraftId, String jobType);
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
spring:
|
||||
application:
|
||||
name: content-service
|
||||
|
||||
datasource:
|
||||
url: jdbc:postgresql://4.217.131.139:5432/contentdb
|
||||
username: eventuser
|
||||
password: Hi5Jessica!
|
||||
driver-class-name: org.postgresql.Driver
|
||||
|
||||
jpa:
|
||||
database-platform: org.hibernate.dialect.PostgreSQLDialect
|
||||
hibernate:
|
||||
ddl-auto: update
|
||||
show-sql: true
|
||||
properties:
|
||||
hibernate:
|
||||
format_sql: true
|
||||
use_sql_comments: true
|
||||
|
||||
data:
|
||||
redis:
|
||||
host: 4.217.131.139
|
||||
port: 6379
|
||||
|
||||
kafka:
|
||||
bootstrap-servers: 20.249.125.115:9092
|
||||
consumer:
|
||||
group-id: content-service-consumers
|
||||
auto-offset-reset: earliest
|
||||
key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
|
||||
value-deserializer: org.apache.kafka.common.serialization.StringDeserializer
|
||||
|
||||
server:
|
||||
port: 8084
|
||||
|
||||
jwt:
|
||||
secret: kt-event-marketing-jwt-secret-key-for-authentication-and-authorization-2025
|
||||
access-token-validity: 3600000
|
||||
refresh-token-validity: 604800000
|
||||
|
||||
azure:
|
||||
storage:
|
||||
connection-string: ${AZURE_STORAGE_CONNECTION_STRING:}
|
||||
container-name: event-images
|
||||
|
||||
logging:
|
||||
level:
|
||||
com.kt.event: DEBUG
|
||||
org.hibernate.SQL: DEBUG
|
||||
Reference in New Issue
Block a user