mirror of
https://github.com/won-ktds/smarketing-backend.git
synced 2026-06-12 20:39:09 +00:00
Merge branch 'main' of https://github.com/won-ktds/smarketing-backend
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
dependencies {
|
||||
implementation project(':common')
|
||||
runtimeOnly 'com.mysql:mysql-connector-j'
|
||||
runtimeOnly 'org.postgresql:postgresql'
|
||||
}
|
||||
+10
-3
@@ -9,9 +9,16 @@ import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||
* 마케팅 콘텐츠 서비스 메인 애플리케이션 클래스
|
||||
* Clean Architecture 패턴을 적용한 마케팅 콘텐츠 관리 서비스
|
||||
*/
|
||||
@SpringBootApplication(scanBasePackages = {"com.won.smarketing.content", "com.won.smarketing.common"})
|
||||
@EntityScan(basePackages = {"com.won.smarketing.content.infrastructure.entity"})
|
||||
@EnableJpaRepositories(basePackages = {"com.won.smarketing.content.infrastructure.repository"})
|
||||
@SpringBootApplication(scanBasePackages = {
|
||||
"com.won.smarketing.content",
|
||||
"com.won.smarketing.common"
|
||||
})
|
||||
@EnableJpaRepositories(basePackages = {
|
||||
"com.won.smarketing.content.infrastructure.repository"
|
||||
})
|
||||
@EntityScan(basePackages = {
|
||||
"com.won.smarketing.content.domain.model"
|
||||
})
|
||||
public class MarketingContentServiceApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
+11
-11
@@ -40,18 +40,18 @@ public class ContentQueryService implements ContentQueryUseCase {
|
||||
|
||||
// 제목과 기간 업데이트
|
||||
content.updateTitle(request.getTitle());
|
||||
content.updatePeriod(request.getStartDate(), request.getEndDate());
|
||||
content.updatePeriod(request.getPromotionStartDate(), request.getPromotionEndDate());
|
||||
|
||||
Content updatedContent = contentRepository.save(content);
|
||||
|
||||
return ContentUpdateResponse.builder()
|
||||
.contentId(updatedContent.getId().getValue())
|
||||
.contentType(updatedContent.getContentType().name())
|
||||
.platform(updatedContent.getPlatform().name())
|
||||
.contentId(updatedContent.getId())
|
||||
//.contentType(updatedContent.getContentType().name())
|
||||
//.platform(updatedContent.getPlatform().name())
|
||||
.title(updatedContent.getTitle())
|
||||
.content(updatedContent.getContent())
|
||||
.hashtags(updatedContent.getHashtags())
|
||||
.images(updatedContent.getImages())
|
||||
//.hashtags(updatedContent.getHashtags())
|
||||
//.images(updatedContent.getImages())
|
||||
.status(updatedContent.getStatus().name())
|
||||
.updatedAt(updatedContent.getUpdatedAt())
|
||||
.build();
|
||||
@@ -105,7 +105,7 @@ public class ContentQueryService implements ContentQueryUseCase {
|
||||
.orElseThrow(() -> new BusinessException(ErrorCode.CONTENT_NOT_FOUND));
|
||||
|
||||
return ContentDetailResponse.builder()
|
||||
.contentId(content.getId().getValue())
|
||||
.contentId(content.getId())
|
||||
.contentType(content.getContentType().name())
|
||||
.platform(content.getPlatform().name())
|
||||
.title(content.getTitle())
|
||||
@@ -140,7 +140,7 @@ public class ContentQueryService implements ContentQueryUseCase {
|
||||
*/
|
||||
private ContentResponse toContentResponse(Content content) {
|
||||
return ContentResponse.builder()
|
||||
.contentId(content.getId().getValue())
|
||||
.contentId(content.getId())
|
||||
.contentType(content.getContentType().name())
|
||||
.platform(content.getPlatform().name())
|
||||
.title(content.getTitle())
|
||||
@@ -161,13 +161,13 @@ public class ContentQueryService implements ContentQueryUseCase {
|
||||
*/
|
||||
private OngoingContentResponse toOngoingContentResponse(Content content) {
|
||||
return OngoingContentResponse.builder()
|
||||
.contentId(content.getId().getValue())
|
||||
.contentId(content.getId())
|
||||
.contentType(content.getContentType().name())
|
||||
.platform(content.getPlatform().name())
|
||||
.title(content.getTitle())
|
||||
.status(content.getStatus().name())
|
||||
.createdAt(content.getCreatedAt())
|
||||
.viewCount(0) // TODO: 실제 조회 수 구현 필요
|
||||
.promotionStartDate(content.getPromotionStartDate())
|
||||
//.viewCount(0) // TODO: 실제 조회 수 구현 필요
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -61,10 +61,10 @@ public class PosterContentService implements PosterContentUseCase {
|
||||
.contentId(null) // 임시 생성이므로 ID 없음
|
||||
.contentType(ContentType.POSTER.name())
|
||||
.title(request.getTitle())
|
||||
.image(generatedPoster)
|
||||
.posterImage(generatedPoster)
|
||||
.posterSizes(posterSizes)
|
||||
.status(ContentStatus.DRAFT.name())
|
||||
.createdAt(LocalDateTime.now())
|
||||
//.createdAt(LocalDateTime.now())
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -39,7 +39,7 @@ public class SnsContentService implements SnsContentUseCase {
|
||||
*/
|
||||
@Override
|
||||
@Transactional
|
||||
/* public SnsContentCreateResponse generateSnsContent(SnsContentCreateRequest request) {
|
||||
public SnsContentCreateResponse generateSnsContent(SnsContentCreateRequest request) {
|
||||
// AI를 사용하여 SNS 콘텐츠 생성
|
||||
String generatedContent = aiContentGenerator.generateSnsContent(request);
|
||||
|
||||
@@ -80,11 +80,11 @@ public class SnsContentService implements SnsContentUseCase {
|
||||
.title(content.getTitle())
|
||||
.content(content.getContent())
|
||||
.hashtags(content.getHashtags())
|
||||
.images(content.getImages())
|
||||
.fixedImages(content.getImages())
|
||||
.status(content.getStatus().name())
|
||||
.createdAt(content.getCreatedAt())
|
||||
.build();
|
||||
}*/
|
||||
}
|
||||
|
||||
/**
|
||||
* SNS 콘텐츠 저장
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
// marketing-content/src/main/java/com/won/smarketing/content/config/JpaConfig.java
|
||||
package com.won.smarketing.content.config;
|
||||
|
||||
import org.springframework.boot.autoconfigure.domain.EntityScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||
|
||||
/**
|
||||
* JPA 설정 클래스
|
||||
*
|
||||
* @author smarketing-team
|
||||
* @version 1.0
|
||||
*/
|
||||
@Configuration
|
||||
@EntityScan(basePackages = "com.won.smarketing.content.infrastructure.entity")
|
||||
@EnableJpaRepositories(basePackages = "com.won.smarketing.content.infrastructure.repository")
|
||||
public class JpaConfig {
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
|
||||
|
||||
// marketing-content/src/main/java/com/won/smarketing/content/config/ObjectMapperConfig.java
|
||||
package com.won.smarketing.content.config;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* ObjectMapper 설정 클래스
|
||||
*
|
||||
* @author smarketing-team
|
||||
* @version 1.0
|
||||
*/
|
||||
@Configuration
|
||||
public class ObjectMapperConfig {
|
||||
|
||||
@Bean
|
||||
public ObjectMapper objectMapper() {
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
objectMapper.registerModule(new JavaTimeModule());
|
||||
return objectMapper;
|
||||
}
|
||||
}
|
||||
+21
@@ -131,6 +131,9 @@ public class Content {
|
||||
@Column(name = "updated_at")
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
public Content(ContentId of, ContentType contentType, Platform platform, String title, String content, List<String> strings, List<String> strings1, ContentStatus contentStatus, CreationConditions conditions, Long storeId, LocalDateTime createdAt, LocalDateTime updatedAt) {
|
||||
}
|
||||
|
||||
// ==================== 비즈니스 로직 메서드 ====================
|
||||
|
||||
/**
|
||||
@@ -216,6 +219,24 @@ public class Content {
|
||||
this.promotionEndDate = endDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* 홍보 기간 설정
|
||||
*
|
||||
* 비즈니스 규칙:
|
||||
* - 시작일은 종료일보다 이전이어야 함
|
||||
* - 과거 날짜로 설정 불가 (현재 시간 기준)
|
||||
*
|
||||
* @param startDate 홍보 시작일
|
||||
* @param endDate 홍보 종료일
|
||||
* @throws IllegalArgumentException 날짜가 유효하지 않은 경우
|
||||
*/
|
||||
public void updatePeriod(LocalDateTime startDate, LocalDateTime endDate) {
|
||||
validatePromotionPeriod(startDate, endDate);
|
||||
|
||||
this.promotionStartDate = startDate;
|
||||
this.promotionEndDate = endDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* 해시태그 추가
|
||||
*
|
||||
|
||||
+2
-5
@@ -1,16 +1,13 @@
|
||||
package com.won.smarketing.content.domain.model;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.*;
|
||||
|
||||
/**
|
||||
* 콘텐츠 식별자 값 객체
|
||||
* 콘텐츠의 고유 식별자를 나타내는 도메인 객체
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor(access = AccessLevel.PROTECTED)
|
||||
@AllArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
@EqualsAndHashCode
|
||||
|
||||
+10
@@ -53,4 +53,14 @@ public class CreationConditions {
|
||||
* 사진 스타일 (포스터용)
|
||||
*/
|
||||
private String photoStyle;
|
||||
|
||||
/**
|
||||
* 타겟 고객
|
||||
*/
|
||||
private String targetAudience;
|
||||
|
||||
/**
|
||||
* 프로모션 타입
|
||||
*/
|
||||
private String promotionType;
|
||||
}
|
||||
|
||||
+2
@@ -4,6 +4,7 @@ import com.won.smarketing.content.domain.model.Content;
|
||||
import com.won.smarketing.content.domain.model.ContentId;
|
||||
import com.won.smarketing.content.domain.model.ContentType;
|
||||
import com.won.smarketing.content.domain.model.Platform;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
@@ -12,6 +13,7 @@ import java.util.Optional;
|
||||
* 콘텐츠 저장소 인터페이스
|
||||
* 콘텐츠 도메인의 데이터 접근 추상화
|
||||
*/
|
||||
@Repository
|
||||
public interface ContentRepository {
|
||||
|
||||
/**
|
||||
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
package com.won.smarketing.content.infrastructure.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
/**
|
||||
* 콘텐츠 조건 JPA 엔티티
|
||||
*
|
||||
* @author smarketing-team
|
||||
* @version 1.0
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "contents_conditions")
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
public class ContentConditionsJpaEntity {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@OneToOne
|
||||
@JoinColumn(name = "content_id")
|
||||
private ContentJpaEntity content;
|
||||
|
||||
@Column(name = "category", length = 100)
|
||||
private String category;
|
||||
|
||||
@Column(name = "requirement", columnDefinition = "TEXT")
|
||||
private String requirement;
|
||||
|
||||
@Column(name = "tone_and_manner", length = 100)
|
||||
private String toneAndManner;
|
||||
|
||||
@Column(name = "emotion_intensity", length = 100)
|
||||
private String emotionIntensity;
|
||||
|
||||
@Column(name = "event_name", length = 200)
|
||||
private String eventName;
|
||||
|
||||
@Column(name = "start_date")
|
||||
private LocalDate startDate;
|
||||
|
||||
@Column(name = "end_date")
|
||||
private LocalDate endDate;
|
||||
|
||||
@Column(name = "photo_style", length = 100)
|
||||
private String photoStyle;
|
||||
|
||||
@Column(name = "TargetAudience", length = 100)
|
||||
private String targetAudience;
|
||||
|
||||
@Column(name = "PromotionType", length = 100)
|
||||
private String PromotionType;
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
// marketing-content/src/main/java/com/won/smarketing/content/infrastructure/entity/ContentJpaEntity.java
|
||||
package com.won.smarketing.content.infrastructure.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import org.hibernate.annotations.CreationTimestamp;
|
||||
import org.hibernate.annotations.UpdateTimestamp;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 콘텐츠 JPA 엔티티
|
||||
*
|
||||
* @author smarketing-team
|
||||
* @version 1.0
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "contents")
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
public class ContentJpaEntity {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(name = "store_id", nullable = false)
|
||||
private Long storeId;
|
||||
|
||||
@Column(name = "content_type", nullable = false, length = 50)
|
||||
private String contentType;
|
||||
|
||||
@Column(name = "platform", length = 50)
|
||||
private String platform;
|
||||
|
||||
@Column(name = "title", length = 500)
|
||||
private String title;
|
||||
|
||||
@Column(name = "content", columnDefinition = "TEXT")
|
||||
private String content;
|
||||
|
||||
@Column(name = "hashtags", columnDefinition = "JSON")
|
||||
private String hashtags;
|
||||
|
||||
@Column(name = "images", columnDefinition = "JSON")
|
||||
private String images;
|
||||
|
||||
@Column(name = "status", length = 50)
|
||||
private String status;
|
||||
|
||||
@CreationTimestamp
|
||||
@Column(name = "created_at")
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
@UpdateTimestamp
|
||||
@Column(name = "updated_at")
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
// 연관 엔티티
|
||||
@OneToOne(mappedBy = "content", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
|
||||
private ContentConditionsJpaEntity conditions;
|
||||
}
|
||||
+144
@@ -0,0 +1,144 @@
|
||||
package com.won.smarketing.content.infrastructure.mapper;
|
||||
|
||||
import com.won.smarketing.content.domain.model.*;
|
||||
import com.won.smarketing.content.infrastructure.entity.ContentConditionsJpaEntity;
|
||||
import com.won.smarketing.content.infrastructure.entity.ContentJpaEntity;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 콘텐츠 도메인-엔티티 매퍼
|
||||
*
|
||||
* @author smarketing-team
|
||||
* @version 1.0
|
||||
*/
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class ContentMapper {
|
||||
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
/**
|
||||
* 도메인 모델을 JPA 엔티티로 변환합니다.
|
||||
*
|
||||
* @param content 도메인 콘텐츠
|
||||
* @return JPA 엔티티
|
||||
*/
|
||||
public ContentJpaEntity toEntity(Content content) {
|
||||
if (content == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ContentJpaEntity entity = new ContentJpaEntity();
|
||||
if (content.getId() != null) {
|
||||
entity.setId(content.getId());
|
||||
}
|
||||
entity.setStoreId(content.getStoreId());
|
||||
entity.setContentType(content.getContentType().name());
|
||||
entity.setPlatform(content.getPlatform() != null ? content.getPlatform().name() : null);
|
||||
entity.setTitle(content.getTitle());
|
||||
entity.setContent(content.getContent());
|
||||
entity.setHashtags(convertListToJson(content.getHashtags()));
|
||||
entity.setImages(convertListToJson(content.getImages()));
|
||||
entity.setStatus(content.getStatus().name());
|
||||
entity.setCreatedAt(content.getCreatedAt());
|
||||
entity.setUpdatedAt(content.getUpdatedAt());
|
||||
|
||||
// 조건 정보 매핑
|
||||
if (content.getCreationConditions() != null) {
|
||||
ContentConditionsJpaEntity conditionsEntity = new ContentConditionsJpaEntity();
|
||||
conditionsEntity.setContent(entity);
|
||||
conditionsEntity.setCategory(content.getCreationConditions().getCategory());
|
||||
conditionsEntity.setRequirement(content.getCreationConditions().getRequirement());
|
||||
conditionsEntity.setToneAndManner(content.getCreationConditions().getToneAndManner());
|
||||
conditionsEntity.setEmotionIntensity(content.getCreationConditions().getEmotionIntensity());
|
||||
conditionsEntity.setEventName(content.getCreationConditions().getEventName());
|
||||
conditionsEntity.setStartDate(content.getCreationConditions().getStartDate());
|
||||
conditionsEntity.setEndDate(content.getCreationConditions().getEndDate());
|
||||
conditionsEntity.setPhotoStyle(content.getCreationConditions().getPhotoStyle());
|
||||
entity.setConditions(conditionsEntity);
|
||||
}
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* JPA 엔티티를 도메인 모델로 변환합니다.
|
||||
*
|
||||
* @param entity JPA 엔티티
|
||||
* @return 도메인 콘텐츠
|
||||
*/
|
||||
public Content toDomain(ContentJpaEntity entity) {
|
||||
if (entity == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
CreationConditions conditions = null;
|
||||
if (entity.getConditions() != null) {
|
||||
conditions = new CreationConditions(
|
||||
entity.getConditions().getCategory(),
|
||||
entity.getConditions().getRequirement(),
|
||||
entity.getConditions().getToneAndManner(),
|
||||
entity.getConditions().getEmotionIntensity(),
|
||||
entity.getConditions().getEventName(),
|
||||
entity.getConditions().getStartDate(),
|
||||
entity.getConditions().getEndDate(),
|
||||
entity.getConditions().getPhotoStyle(),
|
||||
entity.getConditions().getTargetAudience(),
|
||||
entity.getConditions().getPromotionType()
|
||||
);
|
||||
}
|
||||
|
||||
return new Content(
|
||||
ContentId.of(entity.getId()),
|
||||
ContentType.valueOf(entity.getContentType()),
|
||||
entity.getPlatform() != null ? Platform.valueOf(entity.getPlatform()) : null,
|
||||
entity.getTitle(),
|
||||
entity.getContent(),
|
||||
convertJsonToList(entity.getHashtags()),
|
||||
convertJsonToList(entity.getImages()),
|
||||
ContentStatus.valueOf(entity.getStatus()),
|
||||
conditions,
|
||||
entity.getStoreId(),
|
||||
entity.getCreatedAt(),
|
||||
entity.getUpdatedAt()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* List를 JSON 문자열로 변환합니다.
|
||||
*/
|
||||
private String convertListToJson(List<String> list) {
|
||||
if (list == null || list.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return objectMapper.writeValueAsString(list);
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to convert list to JSON: {}", e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON 문자열을 List로 변환합니다.
|
||||
*/
|
||||
private List<String> convertJsonToList(String json) {
|
||||
if (json == null || json.trim().isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
try {
|
||||
return objectMapper.readValue(json, new TypeReference<List<String>>() {});
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to convert JSON to list: {}", e.getMessage());
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
}
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
package com.won.smarketing.content.infrastructure.repository;
|
||||
|
||||
import com.won.smarketing.content.domain.model.Content;
|
||||
import com.won.smarketing.content.domain.model.ContentId;
|
||||
import com.won.smarketing.content.domain.model.ContentType;
|
||||
import com.won.smarketing.content.domain.model.Platform;
|
||||
import com.won.smarketing.content.domain.repository.ContentRepository;
|
||||
import com.won.smarketing.content.infrastructure.entity.ContentJpaEntity;
|
||||
import com.won.smarketing.content.infrastructure.mapper.ContentMapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* JPA 기반 콘텐츠 Repository 구현체
|
||||
*
|
||||
* @author smarketing-team
|
||||
* @version 1.0
|
||||
*/
|
||||
@Repository
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class JpaContentRepository implements ContentRepository {
|
||||
|
||||
private final SpringDataContentRepository springDataContentRepository;
|
||||
private final ContentMapper contentMapper;
|
||||
|
||||
/**
|
||||
* 콘텐츠를 저장합니다.
|
||||
*
|
||||
* @param content 저장할 콘텐츠
|
||||
* @return 저장된 콘텐츠
|
||||
*/
|
||||
@Override
|
||||
public Content save(Content content) {
|
||||
log.debug("Saving content: {}", content.getId());
|
||||
ContentJpaEntity entity = contentMapper.toEntity(content);
|
||||
ContentJpaEntity savedEntity = springDataContentRepository.save(entity);
|
||||
return contentMapper.toDomain(savedEntity);
|
||||
}
|
||||
|
||||
/**
|
||||
* ID로 콘텐츠를 조회합니다.
|
||||
*
|
||||
* @param id 콘텐츠 ID
|
||||
* @return 조회된 콘텐츠
|
||||
*/
|
||||
@Override
|
||||
public Optional<Content> findById(ContentId id) {
|
||||
log.debug("Finding content by id: {}", id.getValue());
|
||||
return springDataContentRepository.findById(id.getValue())
|
||||
.map(contentMapper::toDomain);
|
||||
}
|
||||
|
||||
/**
|
||||
* 필터 조건으로 콘텐츠 목록을 조회합니다.
|
||||
*
|
||||
* @param contentType 콘텐츠 타입
|
||||
* @param platform 플랫폼
|
||||
* @param period 기간
|
||||
* @param sortBy 정렬 기준
|
||||
* @return 콘텐츠 목록
|
||||
*/
|
||||
@Override
|
||||
public List<Content> findByFilters(ContentType contentType, Platform platform, String period, String sortBy) {
|
||||
log.debug("Finding contents by filters - type: {}, platform: {}, period: {}, sortBy: {}",
|
||||
contentType, platform, period, sortBy);
|
||||
|
||||
List<ContentJpaEntity> entities = springDataContentRepository.findByFilters(
|
||||
contentType != null ? contentType.name() : null,
|
||||
platform != null ? platform.name() : null,
|
||||
period,
|
||||
sortBy
|
||||
);
|
||||
|
||||
return entities.stream()
|
||||
.map(contentMapper::toDomain)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 진행 중인 콘텐츠 목록을 조회합니다.
|
||||
*
|
||||
* @param period 기간
|
||||
* @return 진행 중인 콘텐츠 목록
|
||||
*/
|
||||
@Override
|
||||
public List<Content> findOngoingContents(String period) {
|
||||
log.debug("Finding ongoing contents for period: {}", period);
|
||||
List<ContentJpaEntity> entities = springDataContentRepository.findOngoingContents(period);
|
||||
|
||||
return entities.stream()
|
||||
.map(contentMapper::toDomain)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* ID로 콘텐츠를 삭제합니다.
|
||||
*
|
||||
* @param id 콘텐츠 ID
|
||||
*/
|
||||
@Override
|
||||
public void deleteById(ContentId id) {
|
||||
log.debug("Deleting content by id: {}", id.getValue());
|
||||
springDataContentRepository.deleteById(id.getValue());
|
||||
}
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
package com.won.smarketing.content.infrastructure.repository;
|
||||
|
||||
import com.won.smarketing.content.infrastructure.entity.ContentJpaEntity;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Spring Data JPA 콘텐츠 Repository
|
||||
*
|
||||
* @author smarketing-team
|
||||
* @version 1.0
|
||||
*/
|
||||
@Repository
|
||||
public interface SpringDataContentRepository extends JpaRepository<ContentJpaEntity, Long> {
|
||||
|
||||
/**
|
||||
* 필터 조건으로 콘텐츠를 조회합니다.
|
||||
*
|
||||
* @param contentType 콘텐츠 타입
|
||||
* @param platform 플랫폼
|
||||
* @param period 기간
|
||||
* @param sortBy 정렬 기준
|
||||
* @return 콘텐츠 목록
|
||||
*/
|
||||
@Query("SELECT c FROM ContentJpaEntity c WHERE " +
|
||||
"(:contentType IS NULL OR c.contentType = :contentType) AND " +
|
||||
"(:platform IS NULL OR c.platform = :platform) AND " +
|
||||
"(:period IS NULL OR DATE(c.createdAt) >= CURRENT_DATE - INTERVAL :period DAY) " +
|
||||
"ORDER BY " +
|
||||
"CASE WHEN :sortBy = 'latest' THEN c.createdAt END DESC, " +
|
||||
"CASE WHEN :sortBy = 'oldest' THEN c.createdAt END ASC")
|
||||
List<ContentJpaEntity> findByFilters(@Param("contentType") String contentType,
|
||||
@Param("platform") String platform,
|
||||
@Param("period") String period,
|
||||
@Param("sortBy") String sortBy);
|
||||
|
||||
/**
|
||||
* 진행 중인 콘텐츠를 조회합니다.
|
||||
*
|
||||
* @param period 기간
|
||||
* @return 진행 중인 콘텐츠 목록
|
||||
*/
|
||||
@Query("SELECT c FROM ContentJpaEntity c " +
|
||||
"WHERE c.status = 'PUBLISHED' AND " +
|
||||
"(:period IS NULL OR DATE(c.createdAt) >= CURRENT_DATE - INTERVAL :period DAY)")
|
||||
List<ContentJpaEntity> findOngoingContents(@Param("period") String period);
|
||||
}
|
||||
+1
-1
@@ -12,7 +12,7 @@ import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import jakarta.validation.Valid;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
|
||||
+4
-1
@@ -98,6 +98,9 @@ public class ContentResponse {
|
||||
@Schema(description = "해시태그 개수", example = "8")
|
||||
private Integer hashtagCount;
|
||||
|
||||
@Schema(description = "조회수", example = "8")
|
||||
private Integer viewCount;
|
||||
|
||||
// ==================== 비즈니스 메서드 ====================
|
||||
|
||||
/**
|
||||
@@ -227,7 +230,7 @@ public class ContentResponse {
|
||||
*/
|
||||
public static ContentResponse fromDomain(com.won.smarketing.content.domain.model.Content content) {
|
||||
ContentResponseBuilder builder = ContentResponse.builder()
|
||||
.contentId(content.getId().getValue())
|
||||
.contentId(content.getId())
|
||||
.contentType(content.getContentType().name())
|
||||
.platform(content.getPlatform().name())
|
||||
.title(content.getTitle())
|
||||
|
||||
+28
@@ -1,3 +1,4 @@
|
||||
// smarketing-java/marketing-content/src/main/java/com/won/smarketing/content/presentation/dto/PosterContentCreateRequest.java
|
||||
package com.won.smarketing.content.presentation.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
@@ -8,6 +9,7 @@ import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@@ -20,6 +22,13 @@ import java.util.List;
|
||||
@Schema(description = "포스터 콘텐츠 생성 요청")
|
||||
public class PosterContentCreateRequest {
|
||||
|
||||
@Schema(description = "매장 ID", example = "1", required = true)
|
||||
@NotNull(message = "매장 ID는 필수입니다")
|
||||
private Long storeId;
|
||||
|
||||
@Schema(description = "제목", example = "특별 이벤트 안내")
|
||||
private String title;
|
||||
|
||||
@Schema(description = "홍보 대상", example = "메뉴", required = true)
|
||||
@NotBlank(message = "홍보 대상은 필수입니다")
|
||||
private String targetAudience;
|
||||
@@ -48,4 +57,23 @@ public class PosterContentCreateRequest {
|
||||
@NotNull(message = "이미지는 1개 이상 필수입니다")
|
||||
@Size(min = 1, message = "이미지는 1개 이상 업로드해야 합니다")
|
||||
private List<String> images;
|
||||
|
||||
// CreationConditions에 필요한 필드들
|
||||
@Schema(description = "콘텐츠 카테고리", example = "이벤트")
|
||||
private String category;
|
||||
|
||||
@Schema(description = "구체적인 요구사항", example = "신메뉴 출시 이벤트 포스터를 만들어주세요")
|
||||
private String requirement;
|
||||
|
||||
@Schema(description = "톤앤매너", example = "전문적")
|
||||
private String toneAndManner;
|
||||
|
||||
@Schema(description = "이벤트 시작일", example = "2024-01-15")
|
||||
private LocalDate startDate;
|
||||
|
||||
@Schema(description = "이벤트 종료일", example = "2024-01-31")
|
||||
private LocalDate endDate;
|
||||
|
||||
@Schema(description = "사진 스타일", example = "밝고 화사한")
|
||||
private String photoStyle;
|
||||
}
|
||||
+10
-2
@@ -7,6 +7,7 @@ import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 포스터 콘텐츠 생성 응답 DTO
|
||||
@@ -27,8 +28,11 @@ public class PosterContentCreateResponse {
|
||||
@Schema(description = "생성된 포스터 텍스트 내용")
|
||||
private String content;
|
||||
|
||||
@Schema(description = "포스터 이미지 URL 목록")
|
||||
private List<String> posterImages;
|
||||
@Schema(description = "생성된 포스터 타입")
|
||||
private String contentType;
|
||||
|
||||
@Schema(description = "포스터 이미지 URL")
|
||||
private String posterImage;
|
||||
|
||||
@Schema(description = "원본 이미지 URL 목록")
|
||||
private List<String> originalImages;
|
||||
@@ -38,4 +42,8 @@ public class PosterContentCreateResponse {
|
||||
|
||||
@Schema(description = "생성 상태", example = "DRAFT")
|
||||
private String status;
|
||||
|
||||
@Schema(description = "포스터사이즈", example = "800x600")
|
||||
private Map<String, String> posterSizes;
|
||||
|
||||
}
|
||||
+38
-5
@@ -1,3 +1,4 @@
|
||||
// smarketing-java/marketing-content/src/main/java/com/won/smarketing/content/presentation/dto/PosterContentSaveRequest.java
|
||||
package com.won.smarketing.content.presentation.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
@@ -6,6 +7,9 @@ import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 포스터 콘텐츠 저장 요청 DTO
|
||||
*/
|
||||
@@ -19,15 +23,44 @@ public class PosterContentSaveRequest {
|
||||
@NotNull(message = "콘텐츠 ID는 필수입니다")
|
||||
private Long contentId;
|
||||
|
||||
@Schema(description = "최종 제목", example = "특별 이벤트 안내")
|
||||
private String finalTitle;
|
||||
@Schema(description = "매장 ID", example = "1", required = true)
|
||||
@NotNull(message = "매장 ID는 필수입니다")
|
||||
private Long storeId;
|
||||
|
||||
@Schema(description = "최종 콘텐츠 내용")
|
||||
private String finalContent;
|
||||
@Schema(description = "제목", example = "특별 이벤트 안내")
|
||||
private String title;
|
||||
|
||||
@Schema(description = "콘텐츠 내용")
|
||||
private String content;
|
||||
|
||||
@Schema(description = "선택된 포스터 이미지 URL")
|
||||
private String selectedPosterImage;
|
||||
private List<String> images;
|
||||
|
||||
@Schema(description = "발행 상태", example = "PUBLISHED")
|
||||
private String status;
|
||||
|
||||
// CreationConditions에 필요한 필드들
|
||||
@Schema(description = "콘텐츠 카테고리", example = "이벤트")
|
||||
private String category;
|
||||
|
||||
@Schema(description = "구체적인 요구사항", example = "신메뉴 출시 이벤트 포스터를 만들어주세요")
|
||||
private String requirement;
|
||||
|
||||
@Schema(description = "톤앤매너", example = "전문적")
|
||||
private String toneAndManner;
|
||||
|
||||
@Schema(description = "감정 강도", example = "보통")
|
||||
private String emotionIntensity;
|
||||
|
||||
@Schema(description = "이벤트명", example = "신메뉴 출시 이벤트")
|
||||
private String eventName;
|
||||
|
||||
@Schema(description = "이벤트 시작일", example = "2024-01-15")
|
||||
private LocalDate startDate;
|
||||
|
||||
@Schema(description = "이벤트 종료일", example = "2024-01-31")
|
||||
private LocalDate endDate;
|
||||
|
||||
@Schema(description = "사진 스타일", example = "밝고 화사한")
|
||||
private String photoStyle;
|
||||
}
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
package com.won.smarketing.content.presentation.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* SNS 콘텐츠 생성 요청 DTO
|
||||
*
|
||||
* AI 기반 SNS 콘텐츠 생성을 위한 요청 정보를 담고 있습니다.
|
||||
* 사용자가 입력한 생성 조건을 바탕으로 AI가 적절한 SNS 콘텐츠를 생성합니다.
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@Schema(description = "SNS 콘텐츠 생성 요청")
|
||||
public class SnsContentCreateRequest {
|
||||
|
||||
// ==================== 기본 정보 ====================
|
||||
|
||||
@Schema(description = "매장 ID", example = "1", required = true)
|
||||
@NotNull(message = "매장 ID는 필수입니다")
|
||||
private Long storeId;
|
||||
|
||||
@Schema(description = "대상 플랫폼",
|
||||
example = "INSTAGRAM",
|
||||
allowableValues = {"INSTAGRAM", "NAVER_BLOG", "FACEBOOK", "KAKAO_STORY"},
|
||||
required = true)
|
||||
@NotBlank(message = "플랫폼은 필수입니다")
|
||||
private String platform;
|
||||
|
||||
@Schema(description = "콘텐츠 제목", example = "1", required = true)
|
||||
@NotNull(message = "콘텐츠 제목은 필수입니다")
|
||||
private String title;
|
||||
|
||||
// ==================== 콘텐츠 생성 조건 ====================
|
||||
|
||||
@Schema(description = "콘텐츠 카테고리",
|
||||
example = "메뉴소개",
|
||||
allowableValues = {"메뉴소개", "이벤트", "일상", "인테리어", "고객후기", "기타"})
|
||||
private String category;
|
||||
|
||||
@Schema(description = "구체적인 요구사항 또는 홍보하고 싶은 내용",
|
||||
example = "새로 출시된 시그니처 버거를 홍보하고 싶어요")
|
||||
@Size(max = 500, message = "요구사항은 500자 이하로 입력해주세요")
|
||||
private String requirement;
|
||||
|
||||
@Schema(description = "톤앤매너",
|
||||
example = "친근함",
|
||||
allowableValues = {"친근함", "전문적", "유머러스", "감성적", "트렌디"})
|
||||
private String toneAndManner;
|
||||
|
||||
@Schema(description = "감정 강도",
|
||||
example = "보통",
|
||||
allowableValues = {"약함", "보통", "강함"})
|
||||
private String emotionIntensity;
|
||||
|
||||
// ==================== 이벤트 정보 ====================
|
||||
|
||||
@Schema(description = "이벤트명 (이벤트 콘텐츠인 경우)",
|
||||
example = "신메뉴 출시 이벤트")
|
||||
@Size(max = 200, message = "이벤트명은 200자 이하로 입력해주세요")
|
||||
private String eventName;
|
||||
|
||||
@Schema(description = "이벤트 시작일 (이벤트 콘텐츠인 경우)",
|
||||
example = "2024-01-15")
|
||||
private LocalDate startDate;
|
||||
|
||||
@Schema(description = "이벤트 종료일 (이벤트 콘텐츠인 경우)",
|
||||
example = "2024-01-31")
|
||||
private LocalDate endDate;
|
||||
|
||||
// ==================== 미디어 정보 ====================
|
||||
|
||||
@Schema(description = "업로드된 이미지 파일 경로 목록")
|
||||
private List<String> images;
|
||||
|
||||
@Schema(description = "사진 스타일 선호도",
|
||||
example = "밝고 화사한",
|
||||
allowableValues = {"밝고 화사한", "차분하고 세련된", "빈티지한", "모던한", "자연스러운"})
|
||||
private String photoStyle;
|
||||
|
||||
// ==================== 추가 옵션 ====================
|
||||
|
||||
@Schema(description = "해시태그 포함 여부", example = "true")
|
||||
@Builder.Default
|
||||
private Boolean includeHashtags = true;
|
||||
|
||||
@Schema(description = "이모지 포함 여부", example = "true")
|
||||
@Builder.Default
|
||||
private Boolean includeEmojis = true;
|
||||
|
||||
@Schema(description = "콜투액션 포함 여부 (좋아요, 팔로우 요청 등)", example = "true")
|
||||
@Builder.Default
|
||||
private Boolean includeCallToAction = true;
|
||||
|
||||
@Schema(description = "매장 위치 정보 포함 여부", example = "false")
|
||||
@Builder.Default
|
||||
private Boolean includeLocation = false;
|
||||
|
||||
// ==================== 플랫폼별 옵션 ====================
|
||||
|
||||
@Schema(description = "인스타그램 스토리용 여부 (Instagram인 경우)", example = "false")
|
||||
@Builder.Default
|
||||
private Boolean forInstagramStory = false;
|
||||
|
||||
@Schema(description = "네이버 블로그 포스팅용 여부 (Naver Blog인 경우)", example = "false")
|
||||
@Builder.Default
|
||||
private Boolean forNaverBlogPost = false;
|
||||
|
||||
// ==================== AI 생성 옵션 ====================
|
||||
|
||||
@Schema(description = "대안 제목 생성 개수", example = "3")
|
||||
@Builder.Default
|
||||
private Integer alternativeTitleCount = 3;
|
||||
|
||||
@Schema(description = "대안 해시태그 세트 생성 개수", example = "2")
|
||||
@Builder.Default
|
||||
private Integer alternativeHashtagSetCount = 2;
|
||||
|
||||
@Schema(description = "AI 모델 버전 지정 (없으면 기본값 사용)", example = "gpt-4-turbo")
|
||||
private String preferredAiModel;
|
||||
|
||||
// ==================== 검증 메서드 ====================
|
||||
|
||||
/**
|
||||
* 이벤트 날짜 유효성 검증
|
||||
* 시작일이 종료일보다 이후인지 확인
|
||||
*/
|
||||
public boolean isValidEventDates() {
|
||||
if (startDate != null && endDate != null) {
|
||||
return !startDate.isAfter(endDate);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 플랫폼별 필수 조건 검증
|
||||
*/
|
||||
public boolean isValidForPlatform() {
|
||||
if ("INSTAGRAM".equals(platform)) {
|
||||
// 인스타그램은 이미지가 권장됨
|
||||
return images != null && !images.isEmpty();
|
||||
}
|
||||
if ("NAVER_BLOG".equals(platform)) {
|
||||
// 네이버 블로그는 상세한 내용이 필요
|
||||
return requirement != null && requirement.length() >= 20;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+3
@@ -93,6 +93,9 @@ public class SnsContentCreateResponse {
|
||||
@Schema(description = "콘텐츠 카테고리", example = "음식/메뉴소개")
|
||||
private String category;
|
||||
|
||||
@Schema(description = "보정된 이미지 URL 목록")
|
||||
private List<String> fixedImages;
|
||||
|
||||
// ==================== 편집 가능 여부 ====================
|
||||
|
||||
@Schema(description = "제목 편집 가능 여부", example = "true")
|
||||
|
||||
+44
@@ -1,3 +1,4 @@
|
||||
// smarketing-java/marketing-content/src/main/java/com/won/smarketing/content/presentation/dto/SnsContentSaveRequest.java
|
||||
package com.won.smarketing.content.presentation.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
@@ -8,6 +9,7 @@ import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@@ -24,6 +26,26 @@ public class SnsContentSaveRequest {
|
||||
@NotNull(message = "콘텐츠 ID는 필수입니다")
|
||||
private Long contentId;
|
||||
|
||||
@Schema(description = "매장 ID", example = "1", required = true)
|
||||
@NotNull(message = "매장 ID는 필수입니다")
|
||||
private Long storeId;
|
||||
|
||||
@Schema(description = "플랫폼", example = "INSTAGRAM", required = true)
|
||||
@NotBlank(message = "플랫폼은 필수입니다")
|
||||
private String platform;
|
||||
|
||||
@Schema(description = "제목", example = "맛있는 신메뉴를 소개합니다!")
|
||||
private String title;
|
||||
|
||||
@Schema(description = "콘텐츠 내용")
|
||||
private String content;
|
||||
|
||||
@Schema(description = "해시태그 목록")
|
||||
private List<String> hashtags;
|
||||
|
||||
@Schema(description = "이미지 URL 목록")
|
||||
private List<String> images;
|
||||
|
||||
@Schema(description = "최종 제목", example = "맛있는 신메뉴를 소개합니다!")
|
||||
private String finalTitle;
|
||||
|
||||
@@ -32,4 +54,26 @@ public class SnsContentSaveRequest {
|
||||
|
||||
@Schema(description = "발행 상태", example = "PUBLISHED")
|
||||
private String status;
|
||||
|
||||
// CreationConditions에 필요한 필드들
|
||||
@Schema(description = "콘텐츠 카테고리", example = "메뉴소개")
|
||||
private String category;
|
||||
|
||||
@Schema(description = "구체적인 요구사항", example = "새로 출시된 시그니처 버거를 홍보하고 싶어요")
|
||||
private String requirement;
|
||||
|
||||
@Schema(description = "톤앤매너", example = "친근함")
|
||||
private String toneAndManner;
|
||||
|
||||
@Schema(description = "감정 강도", example = "보통")
|
||||
private String emotionIntensity;
|
||||
|
||||
@Schema(description = "이벤트명", example = "신메뉴 출시 이벤트")
|
||||
private String eventName;
|
||||
|
||||
@Schema(description = "이벤트 시작일", example = "2024-01-15")
|
||||
private LocalDate startDate;
|
||||
|
||||
@Schema(description = "이벤트 종료일", example = "2024-01-31")
|
||||
private LocalDate endDate;
|
||||
}
|
||||
@@ -1,15 +1,14 @@
|
||||
server:
|
||||
port: ${SERVER_PORT:8083}
|
||||
servlet:
|
||||
context-path: /
|
||||
|
||||
spring:
|
||||
application:
|
||||
name: marketing-content-service
|
||||
datasource:
|
||||
url: jdbc:postgresql://${POSTGRES_HOST:localhost}:${POSTGRES_PORT:5432}/${POSTGRES_DB:contentdb}
|
||||
url: jdbc:postgresql://${POSTGRES_HOST:localhost}:${POSTGRES_PORT:5432}/${POSTGRES_DB:MarketingContentDB}
|
||||
username: ${POSTGRES_USER:postgres}
|
||||
password: ${POSTGRES_PASSWORD:postgres}
|
||||
driver-class-name: org.postgresql.Driver
|
||||
jpa:
|
||||
hibernate:
|
||||
ddl-auto: ${JPA_DDL_AUTO:update}
|
||||
@@ -29,14 +28,10 @@ external:
|
||||
base-url: ${CLAUDE_AI_BASE_URL:https://api.anthropic.com}
|
||||
model: ${CLAUDE_AI_MODEL:claude-3-sonnet-20240229}
|
||||
max-tokens: ${CLAUDE_AI_MAX_TOKENS:4000}
|
||||
|
||||
springdoc:
|
||||
swagger-ui:
|
||||
path: /swagger-ui.html
|
||||
operations-sorter: method
|
||||
api-docs:
|
||||
path: /api-docs
|
||||
|
||||
jwt:
|
||||
secret: ${JWT_SECRET:mySecretKeyForJWTTokenGenerationAndValidation123456789}
|
||||
access-token-validity: ${JWT_ACCESS_VALIDITY:3600000}
|
||||
refresh-token-validity: ${JWT_REFRESH_VALIDITY:604800000}
|
||||
logging:
|
||||
level:
|
||||
com.won.smarketing.content: ${LOG_LEVEL:DEBUG}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
dependencies {
|
||||
implementation project(':common')
|
||||
runtimeOnly 'com.mysql:mysql-connector-j'
|
||||
// 데이터베이스 의존성
|
||||
runtimeOnly 'org.postgresql:postgresql'
|
||||
}
|
||||
Reference in New Issue
Block a user