talk / backend /sql /045_cafe_merchants.sql
user
Deploy StyleVoiceChat backend
aa9892b
Raw History Blame Contribute Delete
4.01 kB
-- ์นดํŽ˜ ์Œ์„ฑ ์„ ๋ฌผ์ฟ ํฐ MVP โ€” cafe_merchants (2026-05-28, P1a Phase A)
--
-- ์นดํŽ˜ ์‚ฌ์žฅ์ด ๋ณธ์ธ ์นดํŽ˜๋ฅผ ๋“ฑ๋กํ•˜๋Š” ๋ฐœํ–‰์ž ํ…Œ์ด๋ธ”.
-- ์š”์ฒญ์„œ: doc/runbook/req_cafe_gift_mvp.md ยง1
-- ์„ค๊ณ„: doc/runbook/cafe_gift_p1a.md ยง2 sql/045
--
-- ํ•ต์‹ฌ:
-- - ์นดํŽ˜ = ์ฟ ํฐ ๋ฐœํ–‰์žยทํŒ๋งค ๋‹น์‚ฌ์ž (๋‹จ์ผ๊ฐ€๋งน์  โ†’ ์„ ๋ถˆ์—… ๋ฉด์ œ)
-- - status='draft' ์…€ํ”„๋“ฑ๋ก / status='active' ์šด์˜์ž(icesgg) ์ˆ˜๋™ ์ „ํ™˜ (P1a ๋Š” SQL UPDATE)
-- - ํ†ต์‹ ํŒ๋งค์ค‘๊ฐœ ๊ณ ์ง€ ์˜๋ฌด ํ•„๋“œ(์ƒํ˜ธ/๋Œ€ํ‘œ/์ฃผ์†Œ/์ „ํ™”/์‚ฌ์—…์ž๋ฒˆํ˜ธ/ํ†ต์‹ ํŒ๋งค์‹ ๊ณ ๋ฒˆํ˜ธ)
-- - brand_voice = '__default__' or '__spk__<code>' (P1a ๋Š” ๋‚ด์žฅํ™”์ž๋งŒ, voice_clone P2)
--
-- โ›” ํšŒ๊ท€ ๊ฐ€๋“œ:
-- - 2026-05-27 sql/043 ์‚ฌ๊ณ  ๊ตํ›ˆ โ€” ์‹ ๊ทœ ํ…Œ์ด๋ธ”, ๊ธฐ์กด CHECK ์ˆ˜์ • X
-- - CHECK ๋ชจ๋‘ ์‹ ๊ทœ ์ปฌ๋Ÿผ only + nullable/default ๋ช…์‹œ
-- - RLS ํ™œ์„ฑ + owner-scoped + ๊ณต๊ฐœ read ๋Š” status='active' ๋งŒ
CREATE TABLE IF NOT EXISTS public.cafe_merchants (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
owner_user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
-- ๋‹จ์ถ• URL์šฉ (P1a ๋Š” ๋ฏธ์‚ฌ์šฉ NULL, P2 ์— ๋„์ž…)
slug TEXT UNIQUE,
-- ๊ณต๊ฐœ ๋…ธ์ถœ + ํ†ต์‹ ํŒ๋งค์ค‘๊ฐœ ๊ณ ์ง€ (์š”์ฒญ์„œ ยง0-1 #3)
name TEXT NOT NULL, -- ์ƒํ˜ธ๋ช…
representative TEXT, -- ๋Œ€ํ‘œ์ž๋ช…
address TEXT, -- ์‚ฌ์—…์žฅ ์ฃผ์†Œ
phone TEXT, -- ์—ฐ๋ฝ์ฒ˜
business_no TEXT, -- ์‚ฌ์—…์ž๋“ฑ๋ก๋ฒˆํ˜ธ
telesales_no TEXT, -- ํ†ต์‹ ํŒ๋งค์‹ ๊ณ ๋ฒˆํ˜ธ (P1b ํ•„์ˆ˜)
-- ๊ฒฐ์ œ (P1b ์—์„œ ์ฑ„์›€)
pg_submerchant_id TEXT,
-- ์Œ์„ฑํŽธ์ง€ ๊ธฐ๋ณธ ๋ณด์ด์Šค โ€” ์†๋‹˜์ด ๋ณ€๊ฒฝ ๊ฐ€๋Šฅ (Plan ๊ฒฐ์ • #10)
brand_voice TEXT NOT NULL DEFAULT '__default__',
-- ์šด์˜์ž ๊ฒŒ์ดํŠธ (์‚ฌ์šฉ์ž ๊ฒฐ์ • #2 โ€” draft ์…€ํ”„๋“ฑ๋ก + admin ์ˆ˜๋™ active)
status TEXT NOT NULL DEFAULT 'draft'
CHECK (status IN ('draft','active','suspended')),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_cafe_merchants_owner
ON public.cafe_merchants (owner_user_id);
CREATE INDEX IF NOT EXISTS idx_cafe_merchants_status
ON public.cafe_merchants (status);
ALTER TABLE public.cafe_merchants ENABLE ROW LEVEL SECURITY;
-- ์‚ฌ์žฅ: ๋ณธ์ธ ์นดํŽ˜ ALL
DROP POLICY IF EXISTS cafe_merchants_owner ON public.cafe_merchants;
CREATE POLICY cafe_merchants_owner ON public.cafe_merchants
FOR ALL
USING (owner_user_id = auth.uid())
WITH CHECK (owner_user_id = auth.uid());
-- ๊ณต๊ฐœ: status='active' ๋งŒ SELECT (์ค‘๊ฐœ ๊ณ ์ง€ยท๊ตฌ๋งค ํŽ˜์ด์ง€์šฉ, anon ๊ฐ€๋Šฅ)
DROP POLICY IF EXISTS cafe_merchants_public_read ON public.cafe_merchants;
CREATE POLICY cafe_merchants_public_read ON public.cafe_merchants
FOR SELECT
USING (status = 'active');
CREATE OR REPLACE FUNCTION public._cafe_merchants_touch_updated_at()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = NOW();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
DROP TRIGGER IF EXISTS cafe_merchants_touch_updated_at ON public.cafe_merchants;
CREATE TRIGGER cafe_merchants_touch_updated_at
BEFORE UPDATE ON public.cafe_merchants
FOR EACH ROW EXECUTE FUNCTION public._cafe_merchants_touch_updated_at();
COMMENT ON TABLE public.cafe_merchants IS
'์นดํŽ˜ ์Œ์„ฑ ์„ ๋ฌผ์ฟ ํฐ ๋ฐœํ–‰์ž. ๋‹จ์ผ๊ฐ€๋งน์  ์„ ๋ถˆ์—… ๋ฉด์ œ. status=draft ์…€ํ”„๋“ฑ๋ก โ†’ admin ์ˆ˜๋™ active.';
COMMENT ON COLUMN public.cafe_merchants.brand_voice IS
'์Œ์„ฑํŽธ์ง€ ๊ธฐ๋ณธ ๋ณด์ด์Šค: __default__ (Sohee) ๋˜๋Š” __spk__<code> (๋‚ด์žฅํ™”์ž 8์ข…). ์†๋‹˜์ด ๊ตฌ๋งค ์‹œ ๋ณ€๊ฒฝ ๊ฐ€๋Šฅ.';
COMMENT ON COLUMN public.cafe_merchants.status IS
'draft: ์…€ํ”„๋“ฑ๋ก ์งํ›„ / active: ์šด์˜์ž ๊ฒ€์ฆยท๊ฒŒ์‹œ / suspended: ์šด์˜์ž ์ •์ง€.';