Event Service 엔티티와 DDL 형상 일치화 작업

Critical 및 High 우선순위 이슈 수정:

1. Event 엔티티 nullable 필드 변경
   - eventName: nullable로 변경 (AI 추천 후 설정)
   - startDate, endDate: nullable로 변경 (AI 추천 후 설정)

2. Event.publish() 검증 로직 강화
   - eventName 필수 검증 추가
   - startDate, endDate 필수 검증 추가
   - 기간 유효성 검증 추가 (시작일 <= 종료일)

3. DDL 스키마 수정
   - event_name NOT NULL 제거
   - start_date, end_date NOT NULL 제거
   - chk_event_period 제약조건 수정 (NULL 허용)

4. jobs 테이블 외래키 추가
   - event_id에 대한 외래키 제약조건 추가
   - ON DELETE CASCADE 설정으로 데이터 무결성 보장

영향:
- 이벤트 생성 시 eventName, startDate, endDate를 NULL로 허용
- 배포(publish) 시점에 필수 필드 검증으로 데이터 무결성 보장
- 이벤트 삭제 시 관련 Job 자동 삭제로 고아 레코드 방지

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
merrycoral 2025-10-24 13:22:14 +09:00
parent 7b76e573ed
commit c63cf950eb
2 changed files with 18 additions and 7 deletions

View File

@ -22,11 +22,11 @@ CREATE TABLE IF NOT EXISTS events (
event_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), event_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id UUID NOT NULL, user_id UUID NOT NULL,
store_id UUID NOT NULL, store_id UUID NOT NULL,
event_name VARCHAR(200) NOT NULL, event_name VARCHAR(200),
description TEXT, description TEXT,
objective VARCHAR(100) NOT NULL, objective VARCHAR(100) NOT NULL,
start_date DATE NOT NULL, start_date DATE,
end_date DATE NOT NULL, end_date DATE,
status VARCHAR(20) NOT NULL DEFAULT 'DRAFT', status VARCHAR(20) NOT NULL DEFAULT 'DRAFT',
selected_image_id UUID, selected_image_id UUID,
selected_image_url VARCHAR(500), selected_image_url VARCHAR(500),
@ -34,7 +34,7 @@ CREATE TABLE IF NOT EXISTS events (
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
-- 제약조건 -- 제약조건
CONSTRAINT chk_event_period CHECK (start_date <= end_date), CONSTRAINT chk_event_period CHECK (start_date IS NULL OR end_date IS NULL OR start_date <= end_date),
CONSTRAINT chk_event_status CHECK (status IN ('DRAFT', 'PUBLISHED', 'ENDED')) CONSTRAINT chk_event_status CHECK (status IN ('DRAFT', 'PUBLISHED', 'ENDED'))
); );
@ -185,6 +185,8 @@ CREATE TABLE IF NOT EXISTS jobs (
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
-- 제약조건 -- 제약조건
CONSTRAINT fk_jobs_event FOREIGN KEY (event_id)
REFERENCES events(event_id) ON DELETE CASCADE,
CONSTRAINT chk_job_type CHECK (job_type IN ('AI_RECOMMENDATION', 'IMAGE_GENERATION')), CONSTRAINT chk_job_type CHECK (job_type IN ('AI_RECOMMENDATION', 'IMAGE_GENERATION')),
CONSTRAINT chk_job_status CHECK (status IN ('PENDING', 'PROCESSING', 'COMPLETED', 'FAILED')), CONSTRAINT chk_job_status CHECK (status IN ('PENDING', 'PROCESSING', 'COMPLETED', 'FAILED')),
CONSTRAINT chk_job_progress CHECK (progress >= 0 AND progress <= 100) CONSTRAINT chk_job_progress CHECK (progress >= 0 AND progress <= 100)

View File

@ -43,7 +43,7 @@ public class Event extends BaseTimeEntity {
@Column(name = "store_id", nullable = false, columnDefinition = "uuid") @Column(name = "store_id", nullable = false, columnDefinition = "uuid")
private UUID storeId; private UUID storeId;
@Column(name = "event_name", nullable = false, length = 200) @Column(name = "event_name", length = 200)
private String eventName; private String eventName;
@Column(name = "description", columnDefinition = "TEXT") @Column(name = "description", columnDefinition = "TEXT")
@ -52,10 +52,10 @@ public class Event extends BaseTimeEntity {
@Column(name = "objective", nullable = false, length = 100) @Column(name = "objective", nullable = false, length = 100)
private String objective; private String objective;
@Column(name = "start_date", nullable = false) @Column(name = "start_date")
private LocalDate startDate; private LocalDate startDate;
@Column(name = "end_date", nullable = false) @Column(name = "end_date")
private LocalDate endDate; private LocalDate endDate;
@Enumerated(EnumType.STRING) @Enumerated(EnumType.STRING)
@ -147,6 +147,15 @@ public class Event extends BaseTimeEntity {
} }
// 필수 데이터 검증 // 필수 데이터 검증
if (eventName == null || eventName.trim().isEmpty()) {
throw new IllegalStateException("이벤트명을 입력해야 합니다.");
}
if (startDate == null || endDate == null) {
throw new IllegalStateException("이벤트 기간을 설정해야 합니다.");
}
if (startDate.isAfter(endDate)) {
throw new IllegalStateException("시작일은 종료일보다 이전이어야 합니다.");
}
if (selectedImageId == null) { if (selectedImageId == null) {
throw new IllegalStateException("이미지를 선택해야 합니다."); throw new IllegalStateException("이미지를 선택해야 합니다.");
} }