@startuml
!theme mono

title Content Service - ERD (Entity Relationship Diagram)

' ============================================
' PostgreSQL 테이블
' ============================================

entity "content" as content {
  * id : BIGSERIAL <<PK>>
  --
  * event_id : VARCHAR(100) <<UK>>
  * event_title : VARCHAR(200)
  event_description : TEXT
  * created_at : TIMESTAMP
  * updated_at : TIMESTAMP
}

entity "generated_image" as generated_image {
  * id : BIGSERIAL <<PK>>
  --
  * event_id : VARCHAR(100) <<FK>>
  * style : VARCHAR(20) <<CHECK>>
  * platform : VARCHAR(30) <<CHECK>>
  * cdn_url : VARCHAR(500)
  * prompt : TEXT
  * selected : BOOLEAN
  * width : INT
  * height : INT
  file_size : BIGINT
  * content_type : VARCHAR(50)
  * created_at : TIMESTAMP
  * updated_at : TIMESTAMP
  --
  <<INDEX>> (event_id)
  <<INDEX>> (event_id, style, platform)
  <<INDEX>> (created_at)
}

entity "job" as job {
  * id : VARCHAR(100) <<PK>>
  --
  * event_id : VARCHAR(100)
  * job_type : VARCHAR(50) <<CHECK>>
  * status : VARCHAR(20) <<CHECK>>
  * progress : INT
  result_message : TEXT
  error_message : TEXT
  * created_at : TIMESTAMP
  * updated_at : TIMESTAMP
  completed_at : TIMESTAMP
  --
  <<INDEX>> (event_id)
  <<INDEX>> (status, created_at)
}

' ============================================
' Redis 캐시 구조 (개념적 표현)
' ============================================

entity "RedisJobData\n(Cache)" as redis_job {
  * key : job:{jobId}
  --
  id : STRING
  eventId : STRING
  jobType : STRING
  status : STRING
  progress : INT
  resultMessage : STRING
  errorMessage : STRING
  createdAt : TIMESTAMP
  updatedAt : TIMESTAMP
  --
  <<TTL>> 1 hour
}

entity "RedisImageData\n(Cache)" as redis_image {
  * key : image:{eventId}:{style}:{platform}
  --
  eventId : STRING
  style : STRING
  platform : STRING
  imageUrl : STRING
  prompt : STRING
  createdAt : TIMESTAMP
  --
  <<TTL>> 7 days
}

entity "RedisAIEventData\n(Cache)" as redis_ai {
  * key : ai:event:{eventId}
  --
  eventId : STRING
  recommendedStyles : LIST
  recommendedKeywords : LIST
  cachedAt : TIMESTAMP
  --
  <<TTL>> 1 hour
}

' ============================================
' 관계 정의
' ============================================

content ||--o{ generated_image : "has many"
content ||--o{ job : "tracks"

' ============================================
' 캐시 관계 (점선: 논리적 연관)
' ============================================

job ..> redis_job : "cached in"
generated_image ..> redis_image : "cached in"

' ============================================
' 노트 및 설명
' ============================================

note right of content
  **콘텐츠 집합**
  • 이벤트당 하나의 콘텐츠 집합
  • event_id로 이미지 그룹핑
  • 생성/수정 시각 추적
end note

note right of generated_image
  **생성된 이미지 메타데이터**
  • CDN URL만 저장 (Azure Blob)
  • 스타일: FANCY, SIMPLE, TRENDY
  • 플랫폼: INSTAGRAM, FACEBOOK, KAKAO, BLOG
  • 플랫폼별 해상도:
    - INSTAGRAM: 1080x1080
    - FACEBOOK: 1200x628
    - KAKAO: 800x800
    - BLOG: 800x600
  • selected = true: 최종 선택 이미지
end note

note right of job
  **비동기 작업 추적**
  • Job ID: "job-img-{uuid}"
  • 상태: PENDING → PROCESSING → COMPLETED/FAILED
  • progress: 0-100
  • Kafka 기반 비동기 처리
end note

note bottom of redis_job
  **Job 상태 캐싱**
  • 폴링 조회 성능 최적화
  • TTL 1시간 후 자동 삭제
  • PostgreSQL과 동기화
end note

note bottom of redis_image
  **이미지 캐싱**
  • 동일 이벤트 재요청 시 즉시 반환
  • TTL 7일 후 자동 삭제
  • Key: event_id + style + platform
end note

note bottom of redis_ai
  **AI 추천 데이터 캐싱**
  • AI Service 이벤트 분석 결과
  • 추천 스타일 및 키워드
  • TTL 1시간 후 자동 삭제
end note

' ============================================
' 제약 조건 표시
' ============================================

note top of content
  **제약 조건**
  • PK: id (BIGSERIAL)
  • UK: event_id (이벤트당 하나)
  • INDEX: created_at
end note

note top of generated_image
  **제약 조건**
  • PK: id (BIGSERIAL)
  • INDEX: (event_id, style, platform)
  • INDEX: event_id
  • INDEX: created_at
  • CHECK: style IN ('FANCY', 'SIMPLE', 'TRENDY')
  • CHECK: platform IN ('INSTAGRAM', 'FACEBOOK', 'KAKAO', 'BLOG')
  • CHECK: width > 0 AND height > 0
end note

note top of job
  **제약 조건**
  • PK: id (VARCHAR)
  • INDEX: event_id
  • INDEX: (status, created_at)
  • CHECK: status IN ('PENDING', 'PROCESSING', 'COMPLETED', 'FAILED')
  • CHECK: job_type IN ('IMAGE_GENERATION', 'IMAGE_REGENERATION')
  • CHECK: progress >= 0 AND progress <= 100
end note

' ============================================
' 데이터 흐름 및 외부 연동 설명
' ============================================

note as external_integration
  **외부 시스템 연동**
  • Azure Blob Storage (CDN): 이미지 파일 저장
  • Stable Diffusion API: 이미지 생성
  • DALL-E API: Fallback 이미지 생성
  • Kafka Topic (image-generation-job): Job 트리거

  **데이터 흐름**
  1. Kafka에서 이미지 생성 Job 수신
  2. Job 상태 PENDING → PostgreSQL + Redis 저장
  3. AI 추천 데이터 Redis에서 조회
  4. 외부 API 호출 (Stable Diffusion)
  5. 생성된 이미지 CDN 업로드
  6. generated_image 테이블 저장 (CDN URL)
  7. Job 상태 COMPLETED → Redis 업데이트
  8. 클라이언트 폴링 조회 → Redis 캐시 반환
end note

@enduml
