-- 0017 · place_posts — 미니 블로그(AI 자동 포스트). 기획: docs/MINI_BLOG.md -- -- ★ 한 표로 끝내는 이유 — 글의 일생이 "만들어짐 → 검수 → 발송 → 승인 → 게재" 한 줄이라 -- 상태 컬럼 하나면 어디서 멈췄는지가 보인다. 발송함(alert_outbox)을 따로 두지 않는 것도 -- 같은 이유다: 이 글을 몇 시에 누구에게 보냈는지가 글 자체의 속성이다. -- ★ 승인 토큰은 해시만 둔다. 평문은 메일 본문에만 있고 DB 가 새도 링크는 못 쓴다. -- ★ (place_id, topic_key) 유니크가 "같은 축제로 두 번 쓰지 않는다"를 DB 수준에서 강제한다 — -- 프롬프트에만 맡기면 회차가 갈릴 때 같은 주제가 다시 나온다. CREATE TABLE IF NOT EXISTS public.place_posts ( post_id uuid PRIMARY KEY DEFAULT gen_random_uuid(), place_id uuid NOT NULL, body VARCHAR(400) NOT NULL, -- 본문 140~150자 topic_kind SMALLINT NOT NULL, -- PostTopicKind: 1=weather 2=festival 3=season 4=nearby 5=guide topic_key VARCHAR(120) NOT NULL, -- 축제 id · 절기 · 장소 id — 중복 방지의 축 status SMALLINT NOT NULL DEFAULT 1, -- PostStatus: 1=draft 2=reviewed 3=sent 4=approved 5=published 6=skipped approve_token_hash VARCHAR(64) NULL, -- sha256(평문). 평문은 메일에만 token_expires_at TIMESTAMPTZ NULL, sent_at TIMESTAMPTZ NULL, approved_at TIMESTAMPTZ NULL, published_at TIMESTAMPTZ NULL, published_version_id uuid NULL, -- site_versions.site_version_id — 롤백 때 필요 created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), deleted BOOLEAN NOT NULL DEFAULT FALSE ); -- 같은 업장에 같은 주제를 두 번 만들지 않는다. 지운 글은 비켜 준다(재생성 허용). CREATE UNIQUE INDEX IF NOT EXISTS uq_place_posts_topic ON public.place_posts (place_id, topic_key) WHERE deleted = FALSE; -- 화면이 읽는 경로: 그 업장의 게재된 글을 최신순. CREATE INDEX IF NOT EXISTS ix_place_posts_published ON public.place_posts (place_id, published_at DESC) WHERE deleted = FALSE AND status = 5; -- 운영 경로: 검수 대기·발송 대기 목록. CREATE INDEX IF NOT EXISTS ix_place_posts_status ON public.place_posts (status, created_at) WHERE deleted = FALSE; -- 승인 링크가 토큰 해시로 글을 찾는다. CREATE INDEX IF NOT EXISTS ix_place_posts_token ON public.place_posts (approve_token_hash) WHERE approve_token_hash IS NOT NULL;