talk / backend /sql /048_gift_orders.sql
user
Deploy StyleVoiceChat backend
aa9892b
Raw History Blame Contribute Delete
4.54 kB
-- ์นดํŽ˜ ์Œ์„ฑ ์„ ๋ฌผ์ฟ ํฐ MVP โ€” gift_orders (2026-05-28, P1a Phase B)
--
-- ์ฃผ๋ฌธ = ์นดํŽ˜ยท์ƒํ’ˆ + ์Œ์„ฑํŽธ์ง€ + ๋ฐ›๋Š”์ด ์ด๋ฆ„ + ๊ตฌ๋งค์ž ์‹๋ณ„.
-- ์š”์ฒญ์„œ: doc/runbook/req_cafe_gift_mvp.md ยง1
-- ์„ค๊ณ„: doc/runbook/cafe_gift_p1a.md ยง2 sql/048
--
-- Stateless ๊ตฌ๋งค์ž (์‚ฌ์šฉ์ž ๊ฒฐ์ • #1):
-- - buyer_user_id ๋Š” NULL (Supabase anon ๋ฏธ์‚ฌ์šฉ)
-- - ์˜์ˆ˜์ฆ/์žฌ์ ‘๊ทผ = reveal_token + buyer_email
--
-- ๊ฒฐ์ œ mock (P1a):
-- - status 'mock_paid' ๋งŒ coupon ๋ฐœ๊ธ‰. P1b ์—์„œ 'paid' enum ์ถ”๊ฐ€.
-- - mock-pay endpoint ๊ฐ€ ์‹ค PG ์ฝœ๋ฐฑ์„ ๊ฐ€์žฅ (์›Œํ„ฐ๋งˆํฌ๋กœ UI ๋…ธ์ถœ)
--
-- ์œ ํšจ๊ธฐ๊ฐ„ 5๋…„ (๊ณต์ •์œ„ ํ‘œ์ค€์•ฝ๊ด€):
-- - ๋ฐฑ์—”๋“œ๊ฐ€ ๋ช…์‹œ set (DB default X) โ†’ ์ •์ฑ… ๋ณ€๊ฒฝ ์ถ”์  ๊ฐ€๋Šฅ
-- - gift_coupons.expires_at ๋„ ์ด ๊ฐ’์„ ๋ณต์‚ฌ
--
-- โ›” ํšŒ๊ท€ ๊ฐ€๋“œ: ์‹ ๊ทœ ํ…Œ์ด๋ธ”, CHECK ๋ชจ๋‘ ์‹ ๊ทœ ์ปฌ๋Ÿผ๋งŒ. ๊ฒฐ์ œ mock ๋ช…์‹œ ๋ถ„๋ฆฌ ('mock_paid' vs 'paid').
CREATE TABLE IF NOT EXISTS public.gift_orders (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
cafe_id UUID NOT NULL REFERENCES public.cafe_merchants(id) ON DELETE RESTRICT,
product_id UUID NOT NULL REFERENCES public.gift_products(id) ON DELETE RESTRICT,
voice_letter_id UUID REFERENCES public.voice_letters(id) ON DELETE SET NULL,
-- Stateless ๊ตฌ๋งค์ž ์‹๋ณ„ (์‚ฌ์šฉ์ž ๊ฒฐ์ • #1)
buyer_user_id UUID REFERENCES auth.users(id) ON DELETE SET NULL, -- P1a ๋Š” ํ•ญ์ƒ NULL
buyer_name TEXT NOT NULL DEFAULT '',
buyer_phone_e164 TEXT, -- ์˜์ˆ˜์ฆ/ํ™˜๋ถˆ์šฉ (์„ ํƒ)
buyer_email TEXT, -- ์˜์ˆ˜์ฆ ๋ฐœ์†ก (์„ ํƒ)
recipient_name TEXT NOT NULL DEFAULT '',
amount_krw INT NOT NULL CHECK (amount_krw > 0),
-- P1a ๊ฒฐ์ œ mock โ€” 'mock_paid' ๋งŒ coupon ๋ฐœ๊ธ‰. P1b ์—์„œ 'paid' enum ์ •์ƒํ™”.
status TEXT NOT NULL DEFAULT 'pending'
CHECK (status IN ('pending','letter_pending','payable','mock_paid','paid','refunded','canceled')),
pg_tx_id TEXT, -- P1b ์—์„œ ์‹ค PG ๊ฑฐ๋ž˜ ID
-- ์œ ํšจ๊ธฐ๊ฐ„ 5๋…„ (๋ฐฑ์—”๋“œ๊ฐ€ now + 5y ๋ช…์‹œ ์„ธํŒ…, DB default X)
expires_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_gift_orders_cafe_status
ON public.gift_orders (cafe_id, status, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_gift_orders_buyer_user
ON public.gift_orders (buyer_user_id)
WHERE buyer_user_id IS NOT NULL;
-- voice_letters โ†’ orders FK ์–‘๋ฐฉํ–ฅ ๋ณด๊ฐ• (sql/047 ์—์„œ๋Š” column ๋งŒ ๋งŒ๋“ค๊ณ  FK ๋Š” ์—ฌ๊ธฐ)
ALTER TABLE public.voice_letters
DROP CONSTRAINT IF EXISTS voice_letters_order_id_fk;
ALTER TABLE public.voice_letters
ADD CONSTRAINT voice_letters_order_id_fk
FOREIGN KEY (order_id) REFERENCES public.gift_orders(id) ON DELETE SET NULL;
ALTER TABLE public.gift_orders ENABLE ROW LEVEL SECURITY;
-- ์‚ฌ์žฅ: ๋ณธ์ธ ์นดํŽ˜ ์ฃผ๋ฌธ SELECT (๋Œ€์‹œ๋ณด๋“œ์šฉ, Phase E)
DROP POLICY IF EXISTS gift_orders_cafe_read ON public.gift_orders;
CREATE POLICY gift_orders_cafe_read ON public.gift_orders FOR SELECT
USING (cafe_id IN (
SELECT id FROM public.cafe_merchants WHERE owner_user_id = auth.uid()
));
-- ๊ตฌ๋งค์ž ๋ณธ์ธ (๋กœ๊ทธ์ธ ์ผ€์ด์Šค โ€” P1a ๋Š” NULL ์ด๋ผ ๋น„ํ™œ์„ฑ, P1b ํ˜ธํ™˜)
DROP POLICY IF EXISTS gift_orders_buyer_read ON public.gift_orders;
CREATE POLICY gift_orders_buyer_read ON public.gift_orders FOR SELECT
USING (buyer_user_id = auth.uid());
-- INSERT/UPDATE ๋Š” ๋ฐฑ์—”๋“œ service-role ๋งŒ (๋น„๋กœ๊ทธ์ธ ์†๋‹˜ ํ๋ฆ„)
CREATE OR REPLACE FUNCTION public._gift_orders_touch_updated_at()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = NOW();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
DROP TRIGGER IF EXISTS gift_orders_touch_updated_at ON public.gift_orders;
CREATE TRIGGER gift_orders_touch_updated_at
BEFORE UPDATE ON public.gift_orders
FOR EACH ROW EXECUTE FUNCTION public._gift_orders_touch_updated_at();
COMMENT ON TABLE public.gift_orders IS
'์นดํŽ˜ ์„ ๋ฌผ ์ฃผ๋ฌธ. P1a stateless (buyer_user_id NULL), mock_paid ๋งŒ coupon ๋ฐœ๊ธ‰. 5๋…„ ๋งŒ๋ฃŒ.';
COMMENT ON COLUMN public.gift_orders.status IS
'pending โ†’ letter_pending โ†’ payable โ†’ mock_paid (P1a) | paid (P1b) โ†’ refunded/canceled.';
COMMENT ON COLUMN public.gift_orders.expires_at IS
'๊ตฌ๋งค์ผ + 5๋…„ (๊ณต์ •์œ„ ํ‘œ์ค€์•ฝ๊ด€). ๋ฐฑ์—”๋“œ๊ฐ€ ๋ช…์‹œ ์„ธํŒ… (DB default X).';