-- ===================================================== -- 推销模式功能 - 数据库脚本(第一阶段) -- 包含:用户余额系统、分享免单系统、会员等级系统 -- 创建日期: 2026-03-06 -- 注意:请在Supabase SQL编辑器中分段执行 -- ===================================================== -- ===================================================== -- 0. 启用必要扩展 -- ===================================================== CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; CREATE EXTENSION IF NOT EXISTS "pgcrypto"; -- ===================================================== -- 一、用户余额系统 -- ===================================================== -- 1. 用户余额表 DROP TABLE IF EXISTS ml_user_balance CASCADE; CREATE TABLE ml_user_balance ( id UUID DEFAULT uuid_generate_v4() PRIMARY KEY, user_id UUID NOT NULL, balance DECIMAL(10,2) DEFAULT 0, frozen_balance DECIMAL(10,2) DEFAULT 0, total_earned DECIMAL(10,2) DEFAULT 0, total_withdrawn DECIMAL(10,2) DEFAULT 0, updated_at TIMESTAMPTZ DEFAULT NOW(), UNIQUE(user_id) ); CREATE INDEX IF NOT EXISTS idx_user_balance_user_id ON ml_user_balance(user_id); COMMENT ON TABLE ml_user_balance IS '用户余额表'; COMMENT ON COLUMN ml_user_balance.balance IS '可用余额'; COMMENT ON COLUMN ml_user_balance.frozen_balance IS '冻结余额'; COMMENT ON COLUMN ml_user_balance.total_earned IS '累计获得'; COMMENT ON COLUMN ml_user_balance.total_withdrawn IS '累计提现'; -- 2. 余额变动记录表 DROP TABLE IF EXISTS ml_balance_records CASCADE; CREATE TABLE ml_balance_records ( id UUID DEFAULT uuid_generate_v4() PRIMARY KEY, user_id UUID NOT NULL, type VARCHAR(50) NOT NULL, amount DECIMAL(10,2) NOT NULL, balance_before DECIMAL(10,2) NOT NULL DEFAULT 0, balance_after DECIMAL(10,2) NOT NULL DEFAULT 0, related_id UUID, description VARCHAR(200), operator_id UUID, created_at TIMESTAMPTZ DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_balance_records_user_id ON ml_balance_records(user_id); CREATE INDEX IF NOT EXISTS idx_balance_records_type ON ml_balance_records(type); CREATE INDEX IF NOT EXISTS idx_balance_records_created_at ON ml_balance_records(created_at); COMMENT ON TABLE ml_balance_records IS '余额变动记录表'; COMMENT ON COLUMN ml_balance_records.type IS '类型:free_order-免单奖励,rebate-返利,withdraw-提现,clear-清零,manual-手动调整'; -- ===================================================== -- 二、分享免单系统 -- ===================================================== -- 1. 分享记录表 DROP TABLE IF EXISTS ml_share_records CASCADE; CREATE TABLE ml_share_records ( id UUID DEFAULT uuid_generate_v4() PRIMARY KEY, user_id UUID NOT NULL, product_id UUID NOT NULL, order_id UUID NOT NULL, order_item_id UUID, share_code VARCHAR(20) NOT NULL UNIQUE, product_name VARCHAR(200), product_image VARCHAR(500), product_price DECIMAL(10,2), required_count INT DEFAULT 4, current_count INT DEFAULT 0, status INT DEFAULT 0, reward_amount DECIMAL(10,2), created_at TIMESTAMPTZ DEFAULT NOW(), completed_at TIMESTAMPTZ, expired_at TIMESTAMPTZ ); CREATE INDEX IF NOT EXISTS idx_share_records_user_id ON ml_share_records(user_id); CREATE INDEX IF NOT EXISTS idx_share_records_share_code ON ml_share_records(share_code); CREATE INDEX IF NOT EXISTS idx_share_records_status ON ml_share_records(status); COMMENT ON TABLE ml_share_records IS '分享免单记录表'; COMMENT ON COLUMN ml_share_records.status IS '状态:0-进行中,1-已完成,2-已失效,3-已过期'; -- 2. 二级购买记录表 DROP TABLE IF EXISTS ml_secondary_purchases CASCADE; CREATE TABLE ml_secondary_purchases ( id UUID DEFAULT uuid_generate_v4() PRIMARY KEY, share_record_id UUID NOT NULL, buyer_id UUID NOT NULL, order_id UUID NOT NULL, quantity INT DEFAULT 1, unit_price DECIMAL(10,2), created_at TIMESTAMPTZ DEFAULT NOW(), UNIQUE(order_id, share_record_id) ); CREATE INDEX IF NOT EXISTS idx_secondary_purchases_share_record_id ON ml_secondary_purchases(share_record_id); CREATE INDEX IF NOT EXISTS idx_secondary_purchases_buyer_id ON ml_secondary_purchases(buyer_id); COMMENT ON TABLE ml_secondary_purchases IS '二级用户购买记录表'; -- 3. 免单奖励记录表 DROP TABLE IF EXISTS ml_free_order_rewards CASCADE; CREATE TABLE ml_free_order_rewards ( id UUID DEFAULT uuid_generate_v4() PRIMARY KEY, user_id UUID NOT NULL, share_record_id UUID NOT NULL, amount DECIMAL(10,2) NOT NULL, status INT DEFAULT 0, balance_record_id UUID, cleared_at TIMESTAMPTZ, cleared_by UUID, created_at TIMESTAMPTZ DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_free_order_rewards_user_id ON ml_free_order_rewards(user_id); CREATE INDEX IF NOT EXISTS idx_free_order_rewards_status ON ml_free_order_rewards(status); COMMENT ON TABLE ml_free_order_rewards IS '免单奖励记录表'; COMMENT ON COLUMN ml_free_order_rewards.status IS '状态:0-待发放,1-已发放,2-已清零'; -- ===================================================== -- 三、会员等级系统 -- ===================================================== -- 1. 会员等级配置表 DROP TABLE IF EXISTS ml_member_levels CASCADE; CREATE TABLE ml_member_levels ( id INT PRIMARY KEY, name VARCHAR(50) NOT NULL, min_amount DECIMAL(10,2) DEFAULT 0, discount DECIMAL(5,4) DEFAULT 1.0000, icon VARCHAR(200), description TEXT, sort_order INT DEFAULT 0, status INT DEFAULT 1, created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW() ); COMMENT ON TABLE ml_member_levels IS '会员等级配置表'; COMMENT ON COLUMN ml_member_levels.discount IS '折扣率,0.85表示85折'; -- 初始化会员等级数据 INSERT INTO ml_member_levels (id, name, min_amount, discount, description, sort_order) VALUES (0, '普通会员', 0, 1.0000, '注册即可成为普通会员', 0), (1, '铜牌会员', 500, 0.9800, '累计消费500元升级', 1), (2, '银牌会员', 2000, 0.9500, '累计消费2000元升级', 2), (3, '金牌会员', 5000, 0.9200, '累计消费5000元升级', 3), (4, '钻石会员', 10000, 0.8800, '累计消费10000元升级', 4), (5, 'VIP会员', 0, 0.8500, '商家特邀会员', 5); -- 2. 会员等级变更记录表 DROP TABLE IF EXISTS ml_member_level_logs CASCADE; CREATE TABLE ml_member_level_logs ( id UUID DEFAULT uuid_generate_v4() PRIMARY KEY, user_id UUID NOT NULL, old_level INT DEFAULT 0, new_level INT NOT NULL, reason VARCHAR(200), operator_id UUID, created_at TIMESTAMPTZ DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_member_level_logs_user_id ON ml_member_level_logs(user_id); COMMENT ON TABLE ml_member_level_logs IS '会员等级变更记录表'; -- ===================================================== -- 四、RLS 策略 -- ===================================================== -- 用户余额表 RLS ALTER TABLE ml_user_balance ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS "Users can view own balance" ON ml_user_balance; CREATE POLICY "Users can view own balance" ON ml_user_balance FOR SELECT TO authenticated USING (auth.uid() = user_id); DROP POLICY IF EXISTS "Users can insert own balance" ON ml_user_balance; CREATE POLICY "Users can insert own balance" ON ml_user_balance FOR INSERT TO authenticated WITH CHECK (auth.uid() = user_id); DROP POLICY IF EXISTS "Users can update own balance" ON ml_user_balance; CREATE POLICY "Users can update own balance" ON ml_user_balance FOR UPDATE TO authenticated USING (auth.uid() = user_id); -- 余额记录表 RLS ALTER TABLE ml_balance_records ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS "Users can view own balance records" ON ml_balance_records; CREATE POLICY "Users can view own balance records" ON ml_balance_records FOR SELECT TO authenticated USING (auth.uid() = user_id); DROP POLICY IF EXISTS "Users can insert own balance records" ON ml_balance_records; CREATE POLICY "Users can insert own balance records" ON ml_balance_records FOR INSERT TO authenticated WITH CHECK (auth.uid() = user_id); -- 分享记录表 RLS ALTER TABLE ml_share_records ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS "Users can view own share records" ON ml_share_records; CREATE POLICY "Users can view own share records" ON ml_share_records FOR SELECT TO authenticated USING (auth.uid() = user_id); DROP POLICY IF EXISTS "Users can insert own share records" ON ml_share_records; CREATE POLICY "Users can insert own share records" ON ml_share_records FOR INSERT TO authenticated WITH CHECK (auth.uid() = user_id); DROP POLICY IF EXISTS "Users can update own share records" ON ml_share_records; CREATE POLICY "Users can update own share records" ON ml_share_records FOR UPDATE TO authenticated USING (auth.uid() = user_id); -- 允许通过分享码查询 DROP POLICY IF EXISTS "Anyone can view by share code" ON ml_share_records; CREATE POLICY "Anyone can view by share code" ON ml_share_records FOR SELECT TO authenticated, anon USING (share_code IS NOT NULL); -- 二级购买记录表 RLS ALTER TABLE ml_secondary_purchases ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS "Users can view own secondary purchases" ON ml_secondary_purchases; CREATE POLICY "Users can view own secondary purchases" ON ml_secondary_purchases FOR SELECT TO authenticated USING (buyer_id = auth.uid() OR EXISTS ( SELECT 1 FROM ml_share_records WHERE id = share_record_id AND user_id = auth.uid() )); DROP POLICY IF EXISTS "Users can insert secondary purchases" ON ml_secondary_purchases; CREATE POLICY "Users can insert secondary purchases" ON ml_secondary_purchases FOR INSERT TO authenticated WITH CHECK (true); -- 免单奖励记录表 RLS ALTER TABLE ml_free_order_rewards ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS "Users can view own rewards" ON ml_free_order_rewards; CREATE POLICY "Users can view own rewards" ON ml_free_order_rewards FOR SELECT TO authenticated USING (auth.uid() = user_id); -- 会员等级配置表 RLS ALTER TABLE ml_member_levels ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS "Anyone can view member levels" ON ml_member_levels; CREATE POLICY "Anyone can view member levels" ON ml_member_levels FOR SELECT TO authenticated, anon USING (status = 1); -- 会员等级变更记录表 RLS ALTER TABLE ml_member_level_logs ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS "Users can view own level logs" ON ml_member_level_logs; CREATE POLICY "Users can view own level logs" ON ml_member_level_logs FOR SELECT TO authenticated USING (auth.uid() = user_id);