consumer模块完成度95%,检查消费者前端bug并修复

This commit is contained in:
cyh666666
2026-03-09 17:20:59 +08:00
parent 7b5801a72b
commit 2262d1bfd9
128 changed files with 13485 additions and 1670 deletions

View File

@@ -2,22 +2,29 @@
-- 推销模式功能 - 数据库脚本(第一阶段)
-- 包含:用户余额系统、分享免单系统、会员等级系统
-- 创建日期: 2026-03-06
-- 注意请在Supabase SQL编辑器中分段执行
-- =====================================================
-- =====================================================
-- 0. 启用必要扩展
-- =====================================================
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
-- =====================================================
-- 一、用户余额系统
-- =====================================================
-- 1. 用户余额表
CREATE TABLE IF NOT EXISTS ml_user_balance (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
user_id UUID NOT NULL REFERENCES auth.users(id),
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)
);
@@ -30,16 +37,17 @@ COMMENT ON COLUMN ml_user_balance.total_earned IS '累计获得';
COMMENT ON COLUMN ml_user_balance.total_withdrawn IS '累计提现';
-- 2. 余额变动记录表
CREATE TABLE IF NOT EXISTS ml_balance_records (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
user_id UUID NOT NULL REFERENCES auth.users(id),
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 REFERENCES auth.users(id),
operator_id UUID,
created_at TIMESTAMPTZ DEFAULT NOW()
);
@@ -55,11 +63,12 @@ COMMENT ON COLUMN ml_balance_records.type IS '类型free_order-免单奖励
-- =====================================================
-- 1. 分享记录表
CREATE TABLE IF NOT EXISTS ml_share_records (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
user_id UUID NOT NULL REFERENCES auth.users(id),
product_id UUID NOT NULL REFERENCES ml_products(id),
order_id UUID NOT NULL REFERENCES ml_orders(id),
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),
@@ -82,15 +91,15 @@ COMMENT ON TABLE ml_share_records IS '分享免单记录表';
COMMENT ON COLUMN ml_share_records.status IS '状态0-进行中1-已完成2-已失效3-已过期';
-- 2. 二级购买记录表
CREATE TABLE IF NOT EXISTS ml_secondary_purchases (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
share_record_id UUID NOT NULL REFERENCES ml_share_records(id) ON DELETE CASCADE,
buyer_id UUID NOT NULL REFERENCES auth.users(id),
order_id UUID NOT NULL REFERENCES ml_orders(id),
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)
);
@@ -100,15 +109,16 @@ CREATE INDEX IF NOT EXISTS idx_secondary_purchases_buyer_id ON ml_secondary_purc
COMMENT ON TABLE ml_secondary_purchases IS '二级用户购买记录表';
-- 3. 免单奖励记录表
CREATE TABLE IF NOT EXISTS ml_free_order_rewards (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
user_id UUID NOT NULL REFERENCES auth.users(id),
share_record_id UUID NOT NULL REFERENCES ml_share_records(id),
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 REFERENCES ml_balance_records(id),
balance_record_id UUID,
cleared_at TIMESTAMPTZ,
cleared_by UUID REFERENCES auth.users(id),
cleared_by UUID,
created_at TIMESTAMPTZ DEFAULT NOW()
);
@@ -123,7 +133,8 @@ COMMENT ON COLUMN ml_free_order_rewards.status IS '状态0-待发放1-已
-- =====================================================
-- 1. 会员等级配置表
CREATE TABLE IF NOT EXISTS ml_member_levels (
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,
@@ -146,29 +157,17 @@ INSERT INTO ml_member_levels (id, name, min_amount, discount, description, sort_
(2, '银牌会员', 2000, 0.9500, '累计消费2000元升级', 2),
(3, '金牌会员', 5000, 0.9200, '累计消费5000元升级', 3),
(4, '钻石会员', 10000, 0.8800, '累计消费10000元升级', 4),
(5, 'VIP会员', 0, 0.8500, '商家特邀会员', 5)
ON CONFLICT (id) DO NOTHING;
(5, 'VIP会员', 0, 0.8500, '商家特邀会员', 5);
-- 2. 用户会员信息扩展字段(添加到 ml_user_profiles
ALTER TABLE ml_user_profiles ADD COLUMN IF NOT EXISTS member_level INT DEFAULT 0;
ALTER TABLE ml_user_profiles ADD COLUMN IF NOT EXISTS total_spent DECIMAL(10,2) DEFAULT 0;
ALTER TABLE ml_user_profiles ADD COLUMN IF NOT EXISTS level_updated_at TIMESTAMPTZ;
ALTER TABLE ml_user_profiles ADD COLUMN IF NOT EXISTS manual_level BOOLEAN DEFAULT FALSE;
ALTER TABLE ml_user_profiles ADD COLUMN IF NOT EXISTS manual_level_by UUID;
ALTER TABLE ml_user_profiles ADD COLUMN IF NOT EXISTS manual_level_at TIMESTAMPTZ;
COMMENT ON COLUMN ml_user_profiles.member_level IS '当前会员等级';
COMMENT ON COLUMN ml_user_profiles.total_spent IS '累计消费金额';
COMMENT ON COLUMN ml_user_profiles.manual_level IS '是否手动设置等级';
-- 3. 会员等级变更记录表
CREATE TABLE IF NOT EXISTS ml_member_level_logs (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
user_id UUID NOT NULL REFERENCES auth.users(id),
-- 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 REFERENCES auth.users(id),
operator_id UUID,
created_at TIMESTAMPTZ DEFAULT NOW()
);
@@ -183,16 +182,19 @@ COMMENT ON TABLE ml_member_level_logs IS '会员等级变更记录表';
-- 用户余额表 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
@@ -201,11 +203,13 @@ 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
@@ -214,22 +218,26 @@ 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
@@ -238,6 +246,7 @@ 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
@@ -245,6 +254,7 @@ 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
@@ -253,14 +263,16 @@ 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(所有人可查看)
-- 会员等级配置表 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
@@ -269,302 +281,8 @@ 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);
-- =====================================================
-- 五、数据库函数
-- =====================================================
-- 1. 生成分享码函数
CREATE OR REPLACE FUNCTION generate_share_code()
RETURNS VARCHAR(20) AS $$
DECLARE
chars TEXT := 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';
result VARCHAR(20) := '';
i INT;
BEGIN
FOR i IN 1..8 LOOP
result := result || substr(chars, floor(random() * length(chars) + 1)::int, 1);
END LOOP;
RETURN result;
END;
$$ LANGUAGE plpgsql;
-- 2. 增加余额函数
CREATE OR REPLACE FUNCTION add_user_balance(
p_user_id UUID,
p_amount DECIMAL(10,2),
p_type VARCHAR(50),
p_related_id UUID DEFAULT NULL,
p_description VARCHAR(200) DEFAULT NULL,
p_operator_id UUID DEFAULT NULL
)
RETURNS BOOLEAN AS $$
DECLARE
v_balance_before DECIMAL(10,2);
v_balance_after DECIMAL(10,2);
BEGIN
-- 获取当前余额
SELECT COALESCE(balance, 0) INTO v_balance_before
FROM ml_user_balance
WHERE user_id = p_user_id;
IF v_balance_before IS NULL THEN
-- 创建余额记录
INSERT INTO ml_user_balance (user_id, balance, total_earned)
VALUES (p_user_id, p_amount, p_amount);
v_balance_before := 0;
v_balance_after := p_amount;
ELSE
-- 更新余额
UPDATE ml_user_balance
SET balance = balance + p_amount,
total_earned = total_earned + CASE WHEN p_amount > 0 THEN p_amount ELSE 0 END,
updated_at = NOW()
WHERE user_id = p_user_id;
v_balance_after := v_balance_before + p_amount;
END IF;
-- 记录变动
INSERT INTO ml_balance_records (user_id, type, amount, balance_before, balance_after, related_id, description, operator_id)
VALUES (p_user_id, p_type, p_amount, v_balance_before, v_balance_after, p_related_id, p_description, p_operator_id);
RETURN TRUE;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
-- 3. 清零用户余额函数
CREATE OR REPLACE FUNCTION clear_user_balance(
p_user_id UUID,
p_operator_id UUID,
p_description VARCHAR(200) DEFAULT '商家清零余额'
)
RETURNS BOOLEAN AS $$
DECLARE
v_balance_before DECIMAL(10,2);
BEGIN
-- 获取当前余额
SELECT balance INTO v_balance_before
FROM ml_user_balance
WHERE user_id = p_user_id;
IF v_balance_before IS NULL OR v_balance_before = 0 THEN
RETURN FALSE;
END IF;
-- 清零余额
UPDATE ml_user_balance
SET balance = 0,
updated_at = NOW()
WHERE user_id = p_user_id;
-- 记录变动
INSERT INTO ml_balance_records (user_id, type, amount, balance_before, balance_after, description, operator_id)
VALUES (p_user_id, 'clear', -v_balance_before, v_balance_before, 0, p_description, p_operator_id);
RETURN TRUE;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
-- 4. 检查并升级会员等级函数
CREATE OR REPLACE FUNCTION check_and_upgrade_member_level(p_user_id UUID)
RETURNS INT AS $$
DECLARE
v_total_spent DECIMAL(10,2);
v_current_level INT;
v_new_level INT;
v_manual_level BOOLEAN;
BEGIN
-- 获取用户当前信息
SELECT member_level, total_spent, manual_level
INTO v_current_level, v_total_spent, v_manual_level
FROM ml_user_profiles
WHERE user_id = p_user_id;
-- 如果是手动设置的等级,不自动升级
IF v_manual_level = TRUE THEN
RETURN v_current_level;
END IF;
-- 根据消费金额计算新等级
SELECT id INTO v_new_level
FROM ml_member_levels
WHERE status = 1 AND min_amount <= COALESCE(v_total_spent, 0)
ORDER BY min_amount DESC
LIMIT 1;
IF v_new_level IS NULL THEN
v_new_level := 0;
END IF;
-- 如果等级有变化,更新并记录
IF v_new_level > COALESCE(v_current_level, 0) THEN
UPDATE ml_user_profiles
SET member_level = v_new_level,
level_updated_at = NOW()
WHERE user_id = p_user_id;
INSERT INTO ml_member_level_logs (user_id, old_level, new_level, reason)
VALUES (p_user_id, COALESCE(v_current_level, 0), v_new_level, '累计消费自动升级');
RETURN v_new_level;
END IF;
RETURN COALESCE(v_current_level, 0);
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
-- 5. 处理分享购买函数
CREATE OR REPLACE FUNCTION process_share_purchase(
p_share_code VARCHAR(20),
p_buyer_id UUID,
p_order_id UUID,
p_quantity INT,
p_unit_price DECIMAL(10,2)
)
RETURNS BOOLEAN AS $$
DECLARE
v_share_record ml_share_records%ROWTYPE;
v_required_count INT;
v_reward_amount DECIMAL(10,2);
BEGIN
-- 查找分享记录
SELECT * INTO v_share_record
FROM ml_share_records
WHERE share_code = p_share_code AND status = 0;
IF NOT FOUND THEN
RETURN FALSE;
END IF;
-- 不能购买自己分享的商品
IF v_share_record.user_id = p_buyer_id THEN
RETURN FALSE;
END IF;
-- 检查是否已购买过(同一用户对同一分享记录只算一次)
IF EXISTS (SELECT 1 FROM ml_secondary_purchases WHERE share_record_id = v_share_record.id AND buyer_id = p_buyer_id) THEN
RETURN FALSE;
END IF;
-- 记录购买
INSERT INTO ml_secondary_purchases (share_record_id, buyer_id, order_id, quantity, unit_price)
VALUES (v_share_record.id, p_buyer_id, p_order_id, p_quantity, p_unit_price);
-- 更新计数
UPDATE ml_share_records
SET current_count = current_count + 1
WHERE id = v_share_record.id;
-- 检查是否达标
SELECT current_count, required_count INTO v_required_count, v_required_count
FROM ml_share_records WHERE id = v_share_record.id;
IF v_share_record.current_count + 1 >= v_share_record.required_count THEN
-- 计算奖励金额
v_reward_amount := v_share_record.product_price;
-- 更新分享记录状态
UPDATE ml_share_records
SET status = 1, completed_at = NOW(), reward_amount = v_reward_amount
WHERE id = v_share_record.id;
-- 创建奖励记录
INSERT INTO ml_free_order_rewards (user_id, share_record_id, amount)
VALUES (v_share_record.user_id, v_share_record.id, v_reward_amount);
-- 增加用户余额
PERFORM add_user_balance(
v_share_record.user_id,
v_reward_amount,
'free_order',
v_share_record.id,
'分享免单奖励:' || v_share_record.product_name
);
-- 更新奖励记录状态
UPDATE ml_free_order_rewards
SET status = 1
WHERE share_record_id = v_share_record.id;
END IF;
RETURN TRUE;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
-- =====================================================
-- 六、触发器
-- =====================================================
-- 订单完成后更新用户累计消费并检查会员等级
CREATE OR REPLACE FUNCTION update_user_total_spent()
RETURNS TRIGGER AS $$
BEGIN
IF NEW.status = 4 AND (OLD.status != 4 OR OLD.status IS NULL) THEN
-- 更新累计消费
UPDATE ml_user_profiles
SET total_spent = COALESCE(total_spent, 0) + NEW.total_amount
WHERE user_id = NEW.user_id;
-- 检查会员等级
PERFORM check_and_upgrade_member_level(NEW.user_id);
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
-- 创建触发器(如果不存在)
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_trigger WHERE tgname = 'trigger_update_user_total_spent'
) THEN
CREATE TRIGGER trigger_update_user_total_spent
AFTER UPDATE ON ml_orders
FOR EACH ROW
EXECUTE FUNCTION update_user_total_spent();
END IF;
END $$;
-- =====================================================
-- 七、视图
-- =====================================================
-- 用户余额概览视图
CREATE OR REPLACE VIEW ml_user_balance_overview AS
SELECT
ub.user_id,
ub.balance,
ub.frozen_balance,
ub.total_earned,
ub.total_withdrawn,
up.member_level,
ml.name as member_level_name,
ml.discount as member_discount,
up.total_spent
FROM ml_user_balance ub
LEFT JOIN ml_user_profiles up ON ub.user_id = up.user_id
LEFT JOIN ml_member_levels ml ON up.member_level = ml.id;
-- 分享进度视图
CREATE OR REPLACE VIEW ml_share_progress_view AS
SELECT
sr.id,
sr.user_id,
sr.product_id,
sr.product_name,
sr.product_image,
sr.product_price,
sr.share_code,
sr.required_count,
sr.current_count,
sr.status,
sr.reward_amount,
sr.created_at,
sr.completed_at,
sr.required_count - sr.current_count as remaining_count
FROM ml_share_records sr;