File size: 4,006 Bytes
aa9892b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
-- ์นดํŽ˜ ์Œ์„ฑ ์„ ๋ฌผ์ฟ ํฐ 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: ์šด์˜์ž ์ •์ง€.';