293 lines
12 KiB
PL/PgSQL
293 lines
12 KiB
PL/PgSQL
-- Zellyy Finance 데이터베이스 스키마 스크립트
|
|
-- 작성일: 2023년
|
|
|
|
-- UUID 확장 활성화
|
|
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
|
|
|
-- 타임스탬프 함수 생성 (자동 업데이트용)
|
|
CREATE OR REPLACE FUNCTION update_updated_at_column()
|
|
RETURNS TRIGGER AS $$
|
|
BEGIN
|
|
NEW.updated_at = NOW();
|
|
RETURN NEW;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
-- 1. 사용자 테이블
|
|
CREATE TABLE users (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
email TEXT UNIQUE NOT NULL,
|
|
password TEXT NOT NULL,
|
|
full_name TEXT NOT NULL,
|
|
profile_image_url TEXT,
|
|
monthly_income DECIMAL(12, 2),
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
|
);
|
|
|
|
-- 사용자 테이블 업데이트 트리거
|
|
CREATE TRIGGER update_users_updated_at
|
|
BEFORE UPDATE ON users
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION update_updated_at_column();
|
|
|
|
-- 사용자 테이블 RLS 정책
|
|
ALTER TABLE users ENABLE ROW LEVEL SECURITY;
|
|
CREATE POLICY "사용자는 자신의 정보만 볼 수 있음" ON users
|
|
FOR SELECT USING (auth.uid() = id);
|
|
CREATE POLICY "사용자는 자신의 정보만 수정할 수 있음" ON users
|
|
FOR UPDATE USING (auth.uid() = id);
|
|
|
|
-- 2. 카테고리 테이블
|
|
CREATE TABLE categories (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
|
|
name TEXT NOT NULL,
|
|
color TEXT NOT NULL,
|
|
icon TEXT,
|
|
is_income BOOLEAN DEFAULT FALSE,
|
|
is_default BOOLEAN DEFAULT FALSE,
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
UNIQUE(user_id, name)
|
|
);
|
|
|
|
-- 카테고리 테이블 업데이트 트리거
|
|
CREATE TRIGGER update_categories_updated_at
|
|
BEFORE UPDATE ON categories
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION update_updated_at_column();
|
|
|
|
-- 카테고리 테이블 RLS 정책
|
|
ALTER TABLE categories ENABLE ROW LEVEL SECURITY;
|
|
CREATE POLICY "사용자는 자신의 카테고리와 기본 카테고리를 볼 수 있음" ON categories
|
|
FOR SELECT USING (auth.uid() = user_id OR is_default = TRUE);
|
|
CREATE POLICY "사용자는 자신의 카테고리만 수정할 수 있음" ON categories
|
|
FOR UPDATE USING (auth.uid() = user_id AND is_default = FALSE);
|
|
CREATE POLICY "사용자는 자신의 카테고리만 삭제할 수 있음" ON categories
|
|
FOR DELETE USING (auth.uid() = user_id AND is_default = FALSE);
|
|
CREATE POLICY "사용자는 자신의 카테고리만 생성할 수 있음" ON categories
|
|
FOR INSERT WITH CHECK (auth.uid() = user_id);
|
|
|
|
-- 3. 카드 테이블
|
|
CREATE TABLE cards (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
name TEXT NOT NULL,
|
|
card_number TEXT,
|
|
card_type TEXT NOT NULL,
|
|
bank_name TEXT,
|
|
color TEXT,
|
|
billing_day INTEGER,
|
|
payment_day INTEGER,
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
UNIQUE(user_id, name)
|
|
);
|
|
|
|
-- 카드 테이블 업데이트 트리거
|
|
CREATE TRIGGER update_cards_updated_at
|
|
BEFORE UPDATE ON cards
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION update_updated_at_column();
|
|
|
|
-- 카드 테이블 RLS 정책
|
|
ALTER TABLE cards ENABLE ROW LEVEL SECURITY;
|
|
CREATE POLICY "사용자는 자신의 카드만 볼 수 있음" ON cards
|
|
FOR SELECT USING (auth.uid() = user_id);
|
|
CREATE POLICY "사용자는 자신의 카드만 수정할 수 있음" ON cards
|
|
FOR UPDATE USING (auth.uid() = user_id);
|
|
CREATE POLICY "사용자는 자신의 카드만 삭제할 수 있음" ON cards
|
|
FOR DELETE USING (auth.uid() = user_id);
|
|
CREATE POLICY "사용자는 자신의 카드만 생성할 수 있음" ON cards
|
|
FOR INSERT WITH CHECK (auth.uid() = user_id);
|
|
|
|
-- 4. 한도 테이블
|
|
CREATE TABLE limits (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
card_id UUID REFERENCES cards(id) ON DELETE CASCADE,
|
|
category_id UUID REFERENCES categories(id) ON DELETE SET NULL,
|
|
amount DECIMAL(12, 2) NOT NULL,
|
|
period TEXT NOT NULL, -- 'monthly', 'weekly', 'daily'
|
|
start_date DATE NOT NULL,
|
|
end_date DATE,
|
|
is_active BOOLEAN DEFAULT TRUE,
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
CONSTRAINT card_or_category_required CHECK (card_id IS NOT NULL OR category_id IS NOT NULL)
|
|
);
|
|
|
|
-- 한도 테이블 업데이트 트리거
|
|
CREATE TRIGGER update_limits_updated_at
|
|
BEFORE UPDATE ON limits
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION update_updated_at_column();
|
|
|
|
-- 한도 테이블 RLS 정책
|
|
ALTER TABLE limits ENABLE ROW LEVEL SECURITY;
|
|
CREATE POLICY "사용자는 자신의 한도만 볼 수 있음" ON limits
|
|
FOR SELECT USING (auth.uid() = user_id);
|
|
CREATE POLICY "사용자는 자신의 한도만 수정할 수 있음" ON limits
|
|
FOR UPDATE USING (auth.uid() = user_id);
|
|
CREATE POLICY "사용자는 자신의 한도만 삭제할 수 있음" ON limits
|
|
FOR DELETE USING (auth.uid() = user_id);
|
|
CREATE POLICY "사용자는 자신의 한도만 생성할 수 있음" ON limits
|
|
FOR INSERT WITH CHECK (auth.uid() = user_id);
|
|
|
|
-- 5. 지출 테이블
|
|
CREATE TABLE expenses (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
card_id UUID REFERENCES cards(id) ON DELETE SET NULL,
|
|
category_id UUID REFERENCES categories(id) ON DELETE SET NULL,
|
|
amount DECIMAL(12, 2) NOT NULL,
|
|
description TEXT,
|
|
date TIMESTAMP WITH TIME ZONE NOT NULL,
|
|
location TEXT,
|
|
receipt_image_url TEXT,
|
|
is_income BOOLEAN DEFAULT FALSE,
|
|
is_recurring BOOLEAN DEFAULT FALSE,
|
|
recurring_period TEXT, -- 'monthly', 'weekly', 'daily'
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
|
);
|
|
|
|
-- 지출 테이블 업데이트 트리거
|
|
CREATE TRIGGER update_expenses_updated_at
|
|
BEFORE UPDATE ON expenses
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION update_updated_at_column();
|
|
|
|
-- 지출 테이블 RLS 정책
|
|
ALTER TABLE expenses ENABLE ROW LEVEL SECURITY;
|
|
CREATE POLICY "사용자는 자신의 지출만 볼 수 있음" ON expenses
|
|
FOR SELECT USING (auth.uid() = user_id);
|
|
CREATE POLICY "사용자는 자신의 지출만 수정할 수 있음" ON expenses
|
|
FOR UPDATE USING (auth.uid() = user_id);
|
|
CREATE POLICY "사용자는 자신의 지출만 삭제할 수 있음" ON expenses
|
|
FOR DELETE USING (auth.uid() = user_id);
|
|
CREATE POLICY "사용자는 자신의 지출만 생성할 수 있음" ON expenses
|
|
FOR INSERT WITH CHECK (auth.uid() = user_id);
|
|
|
|
-- 6. 템플릿 테이블
|
|
CREATE TABLE templates (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
name TEXT NOT NULL,
|
|
card_id UUID REFERENCES cards(id) ON DELETE SET NULL,
|
|
category_id UUID REFERENCES categories(id) ON DELETE SET NULL,
|
|
amount DECIMAL(12, 2) NOT NULL,
|
|
description TEXT,
|
|
location TEXT,
|
|
is_income BOOLEAN DEFAULT FALSE,
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
UNIQUE(user_id, name)
|
|
);
|
|
|
|
-- 템플릿 테이블 업데이트 트리거
|
|
CREATE TRIGGER update_templates_updated_at
|
|
BEFORE UPDATE ON templates
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION update_updated_at_column();
|
|
|
|
-- 템플릿 테이블 RLS 정책
|
|
ALTER TABLE templates ENABLE ROW LEVEL SECURITY;
|
|
CREATE POLICY "사용자는 자신의 템플릿만 볼 수 있음" ON templates
|
|
FOR SELECT USING (auth.uid() = user_id);
|
|
CREATE POLICY "사용자는 자신의 템플릿만 수정할 수 있음" ON templates
|
|
FOR UPDATE USING (auth.uid() = user_id);
|
|
CREATE POLICY "사용자는 자신의 템플릿만 삭제할 수 있음" ON templates
|
|
FOR DELETE USING (auth.uid() = user_id);
|
|
CREATE POLICY "사용자는 자신의 템플릿만 생성할 수 있음" ON templates
|
|
FOR INSERT WITH CHECK (auth.uid() = user_id);
|
|
|
|
-- 7. 알림 테이블
|
|
CREATE TABLE notifications (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
title TEXT NOT NULL,
|
|
message TEXT NOT NULL,
|
|
type TEXT NOT NULL, -- 'limit_warning', 'payment_due', 'tip', 'system'
|
|
related_id UUID, -- 관련된 항목의 ID (카드, 한도 등)
|
|
is_read BOOLEAN DEFAULT FALSE,
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
|
);
|
|
|
|
-- 알림 테이블 업데이트 트리거
|
|
CREATE TRIGGER update_notifications_updated_at
|
|
BEFORE UPDATE ON notifications
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION update_updated_at_column();
|
|
|
|
-- 알림 테이블 RLS 정책
|
|
ALTER TABLE notifications ENABLE ROW LEVEL SECURITY;
|
|
CREATE POLICY "사용자는 자신의 알림만 볼 수 있음" ON notifications
|
|
FOR SELECT USING (auth.uid() = user_id);
|
|
CREATE POLICY "사용자는 자신의 알림만 수정할 수 있음" ON notifications
|
|
FOR UPDATE USING (auth.uid() = user_id);
|
|
CREATE POLICY "사용자는 자신의 알림만 삭제할 수 있음" ON notifications
|
|
FOR DELETE USING (auth.uid() = user_id);
|
|
CREATE POLICY "시스템만 알림을 생성할 수 있음" ON notifications
|
|
FOR INSERT WITH CHECK (auth.uid() = user_id OR auth.uid() IS NULL);
|
|
|
|
-- 8. 분석 설정 테이블
|
|
CREATE TABLE analysis_settings (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
budget_period TEXT DEFAULT 'monthly', -- 'monthly', 'weekly', 'daily'
|
|
saving_goal DECIMAL(12, 2),
|
|
analysis_period INTEGER DEFAULT 3, -- 분석에 사용할 과거 데이터 기간 (개월)
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
UNIQUE(user_id)
|
|
);
|
|
|
|
-- 분석 설정 테이블 업데이트 트리거
|
|
CREATE TRIGGER update_analysis_settings_updated_at
|
|
BEFORE UPDATE ON analysis_settings
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION update_updated_at_column();
|
|
|
|
-- 분석 설정 테이블 RLS 정책
|
|
ALTER TABLE analysis_settings ENABLE ROW LEVEL SECURITY;
|
|
CREATE POLICY "사용자는 자신의 분석 설정만 볼 수 있음" ON analysis_settings
|
|
FOR SELECT USING (auth.uid() = user_id);
|
|
CREATE POLICY "사용자는 자신의 분석 설정만 수정할 수 있음" ON analysis_settings
|
|
FOR UPDATE USING (auth.uid() = user_id);
|
|
CREATE POLICY "사용자는 자신의 분석 설정만 삭제할 수 있음" ON analysis_settings
|
|
FOR DELETE USING (auth.uid() = user_id);
|
|
CREATE POLICY "사용자는 자신의 분석 설정만 생성할 수 있음" ON analysis_settings
|
|
FOR INSERT WITH CHECK (auth.uid() = user_id);
|
|
|
|
-- 기본 카테고리 데이터 삽입
|
|
INSERT INTO categories (name, color, icon, is_income, is_default) VALUES
|
|
('식비', '#FF5722', 'restaurant', FALSE, TRUE),
|
|
('교통', '#2196F3', 'directions_car', FALSE, TRUE),
|
|
('주거/통신', '#9C27B0', 'home', FALSE, TRUE),
|
|
('쇼핑', '#4CAF50', 'shopping_bag', FALSE, TRUE),
|
|
('의료/건강', '#F44336', 'local_hospital', FALSE, TRUE),
|
|
('문화/여가', '#FFEB3B', 'movie', FALSE, TRUE),
|
|
('교육', '#795548', 'school', FALSE, TRUE),
|
|
('경조사/기부', '#607D8B', 'card_giftcard', FALSE, TRUE),
|
|
('기타', '#9E9E9E', 'more_horiz', FALSE, TRUE),
|
|
('급여', '#4CAF50', 'payments', TRUE, TRUE),
|
|
('용돈', '#8BC34A', 'savings', TRUE, TRUE),
|
|
('상여금', '#CDDC39', 'card_giftcard', TRUE, TRUE),
|
|
('환불', '#FFC107', 'replay', TRUE, TRUE),
|
|
('투자수익', '#00BCD4', 'trending_up', TRUE, TRUE),
|
|
('기타수입', '#9E9E9E', 'more_horiz', TRUE, TRUE);
|
|
|
|
-- 인덱스 생성
|
|
CREATE INDEX idx_expenses_user_id ON expenses(user_id);
|
|
CREATE INDEX idx_expenses_date ON expenses(date);
|
|
CREATE INDEX idx_expenses_category_id ON expenses(category_id);
|
|
CREATE INDEX idx_expenses_card_id ON expenses(card_id);
|
|
CREATE INDEX idx_limits_user_id ON limits(user_id);
|
|
CREATE INDEX idx_cards_user_id ON cards(user_id);
|
|
CREATE INDEX idx_categories_user_id ON categories(user_id);
|
|
CREATE INDEX idx_notifications_user_id ON notifications(user_id);
|
|
CREATE INDEX idx_notifications_is_read ON notifications(is_read);
|