consumer模块完成度95%,检查消费者前端bug并修复
This commit is contained in:
@@ -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;
|
||||
|
||||
453
doc_mall/consumer/sql/promotion_system_tables_v2.sql
Normal file
453
doc_mall/consumer/sql/promotion_system_tables_v2.sql
Normal file
@@ -0,0 +1,453 @@
|
||||
-- =====================================================
|
||||
-- 推销模式功能 - 数据库脚本 V2(商家级别)
|
||||
-- 包含:商家推销配置、用户余额系统、分享免单系统、会员等级系统
|
||||
-- 创建日期: 2026-03-09
|
||||
-- 重要变更:推销模式改为商家级别,每个商家独立配置
|
||||
-- =====================================================
|
||||
|
||||
-- =====================================================
|
||||
-- 0. 启用必要扩展
|
||||
-- =====================================================
|
||||
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
||||
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
|
||||
|
||||
-- =====================================================
|
||||
-- 一、商家推销配置表
|
||||
-- =====================================================
|
||||
|
||||
DROP TABLE IF EXISTS ml_merchant_promotion_config CASCADE;
|
||||
CREATE TABLE ml_merchant_promotion_config (
|
||||
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
|
||||
merchant_id UUID NOT NULL UNIQUE,
|
||||
promotion_enabled BOOLEAN DEFAULT FALSE,
|
||||
share_free_enabled BOOLEAN DEFAULT FALSE,
|
||||
distribution_enabled BOOLEAN DEFAULT FALSE,
|
||||
required_count INT DEFAULT 4,
|
||||
reward_type VARCHAR(20) DEFAULT 'product_price',
|
||||
fixed_reward_amount DECIMAL(10,2) DEFAULT 0,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_merchant_promotion_merchant_id ON ml_merchant_promotion_config(merchant_id);
|
||||
|
||||
COMMENT ON TABLE ml_merchant_promotion_config IS '商家推销模式配置表';
|
||||
COMMENT ON COLUMN ml_merchant_promotion_config.promotion_enabled IS '是否开启推销模式';
|
||||
COMMENT ON COLUMN ml_merchant_promotion_config.share_free_enabled IS '是否开启分享免单';
|
||||
COMMENT ON COLUMN ml_merchant_promotion_config.distribution_enabled IS '是否开启经销点返利';
|
||||
COMMENT ON COLUMN ml_merchant_promotion_config.required_count IS '分享免单所需购买数';
|
||||
COMMENT ON COLUMN ml_merchant_promotion_config.reward_type IS '奖励类型:product_price-商品价格,fixed-固定金额';
|
||||
|
||||
-- =====================================================
|
||||
-- 二、用户余额系统(按商家区分)
|
||||
-- =====================================================
|
||||
|
||||
-- 1. 商家用户余额表
|
||||
DROP TABLE IF EXISTS ml_merchant_user_balance CASCADE;
|
||||
CREATE TABLE ml_merchant_user_balance (
|
||||
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
|
||||
merchant_id UUID NOT NULL,
|
||||
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(merchant_id, user_id)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_merchant_user_balance_merchant_id ON ml_merchant_user_balance(merchant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_merchant_user_balance_user_id ON ml_merchant_user_balance(user_id);
|
||||
|
||||
COMMENT ON TABLE ml_merchant_user_balance IS '商家用户余额表(每个商家独立余额)';
|
||||
|
||||
-- 2. 余额变动记录表
|
||||
DROP TABLE IF EXISTS ml_balance_records CASCADE;
|
||||
CREATE TABLE ml_balance_records (
|
||||
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
|
||||
merchant_id UUID NOT NULL,
|
||||
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_merchant_id ON ml_balance_records(merchant_id);
|
||||
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,
|
||||
merchant_id UUID NOT NULL,
|
||||
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_merchant_id ON ml_share_records(merchant_id);
|
||||
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,
|
||||
merchant_id UUID NOT NULL,
|
||||
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_merchant_id ON ml_free_order_rewards(merchant_id);
|
||||
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_merchant_promotion_config ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
DROP POLICY IF EXISTS "Anyone can view merchant promotion config" ON ml_merchant_promotion_config;
|
||||
CREATE POLICY "Anyone can view merchant promotion config"
|
||||
ON ml_merchant_promotion_config FOR SELECT
|
||||
TO authenticated, anon
|
||||
USING (true);
|
||||
|
||||
-- 商家用户余额表 RLS
|
||||
ALTER TABLE ml_merchant_user_balance ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
DROP POLICY IF EXISTS "Users can view own merchant balance" ON ml_merchant_user_balance;
|
||||
CREATE POLICY "Users can view own merchant balance"
|
||||
ON ml_merchant_user_balance FOR SELECT
|
||||
TO authenticated
|
||||
USING (auth.uid() = user_id);
|
||||
|
||||
DROP POLICY IF EXISTS "Users can insert own merchant balance" ON ml_merchant_user_balance;
|
||||
CREATE POLICY "Users can insert own merchant balance"
|
||||
ON ml_merchant_user_balance FOR INSERT
|
||||
TO authenticated
|
||||
WITH CHECK (auth.uid() = user_id);
|
||||
|
||||
DROP POLICY IF EXISTS "Users can update own merchant balance" ON ml_merchant_user_balance;
|
||||
CREATE POLICY "Users can update own merchant balance"
|
||||
ON ml_merchant_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);
|
||||
|
||||
-- =====================================================
|
||||
-- 六、数据库函数
|
||||
-- =====================================================
|
||||
|
||||
-- 1. 获取商家推销配置
|
||||
CREATE OR REPLACE FUNCTION get_merchant_promotion_config(p_merchant_id UUID)
|
||||
RETURNS TABLE (
|
||||
promotion_enabled BOOLEAN,
|
||||
share_free_enabled BOOLEAN,
|
||||
distribution_enabled BOOLEAN,
|
||||
required_count INT,
|
||||
reward_type VARCHAR,
|
||||
fixed_reward_amount DECIMAL
|
||||
) AS $$
|
||||
BEGIN
|
||||
RETURN QUERY
|
||||
SELECT
|
||||
mpc.promotion_enabled,
|
||||
mpc.share_free_enabled,
|
||||
mpc.distribution_enabled,
|
||||
mpc.required_count,
|
||||
mpc.reward_type,
|
||||
mpc.fixed_reward_amount
|
||||
FROM ml_merchant_promotion_config mpc
|
||||
WHERE mpc.merchant_id = p_merchant_id;
|
||||
|
||||
IF NOT FOUND THEN
|
||||
RETURN QUERY SELECT FALSE, FALSE, FALSE, 4, 'product_price'::VARCHAR, 0::DECIMAL;
|
||||
END IF;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
||||
|
||||
-- 2. 检查商家是否开启分享免单
|
||||
CREATE OR REPLACE FUNCTION is_share_free_enabled(p_merchant_id UUID)
|
||||
RETURNS BOOLEAN AS $$
|
||||
DECLARE
|
||||
v_enabled BOOLEAN;
|
||||
BEGIN
|
||||
SELECT promotion_enabled AND share_free_enabled INTO v_enabled
|
||||
FROM ml_merchant_promotion_config
|
||||
WHERE merchant_id = p_merchant_id;
|
||||
|
||||
RETURN COALESCE(v_enabled, FALSE);
|
||||
END;
|
||||
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
||||
|
||||
-- 3. 增加商家用户余额
|
||||
CREATE OR REPLACE FUNCTION add_merchant_user_balance(
|
||||
p_merchant_id UUID,
|
||||
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_merchant_user_balance
|
||||
WHERE merchant_id = p_merchant_id AND user_id = p_user_id;
|
||||
|
||||
IF v_balance_before IS NULL THEN
|
||||
INSERT INTO ml_merchant_user_balance (merchant_id, user_id, balance, total_earned)
|
||||
VALUES (p_merchant_id, p_user_id, p_amount, p_amount);
|
||||
v_balance_before := 0;
|
||||
v_balance_after := p_amount;
|
||||
ELSE
|
||||
UPDATE ml_merchant_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 merchant_id = p_merchant_id AND user_id = p_user_id;
|
||||
v_balance_after := v_balance_before + p_amount;
|
||||
END IF;
|
||||
|
||||
INSERT INTO ml_balance_records (merchant_id, user_id, type, amount, balance_before, balance_after, related_id, description, operator_id)
|
||||
VALUES (p_merchant_id, 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;
|
||||
|
||||
-- 4. 清零商家用户余额
|
||||
CREATE OR REPLACE FUNCTION clear_merchant_user_balance(
|
||||
p_merchant_id UUID,
|
||||
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_merchant_user_balance
|
||||
WHERE merchant_id = p_merchant_id AND user_id = p_user_id;
|
||||
|
||||
IF v_balance_before IS NULL OR v_balance_before = 0 THEN
|
||||
RETURN FALSE;
|
||||
END IF;
|
||||
|
||||
UPDATE ml_merchant_user_balance
|
||||
SET balance = 0,
|
||||
updated_at = NOW()
|
||||
WHERE merchant_id = p_merchant_id AND user_id = p_user_id;
|
||||
|
||||
INSERT INTO ml_balance_records (merchant_id, user_id, type, amount, balance_before, balance_after, description, operator_id)
|
||||
VALUES (p_merchant_id, p_user_id, 'clear', -v_balance_before, v_balance_before, 0, p_description, p_operator_id);
|
||||
|
||||
RETURN TRUE;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
||||
|
||||
-- =====================================================
|
||||
-- 七、测试数据(可选)
|
||||
-- =====================================================
|
||||
|
||||
-- 为某个商家开启推销模式(替换YOUR_MERCHANT_ID为实际商家ID)
|
||||
-- INSERT INTO ml_merchant_promotion_config (merchant_id, promotion_enabled, share_free_enabled, required_count)
|
||||
-- VALUES ('YOUR_MERCHANT_ID', TRUE, TRUE, 4);
|
||||
143
doc_mall/consumer/sql/share_free_notifications.sql
Normal file
143
doc_mall/consumer/sql/share_free_notifications.sql
Normal file
@@ -0,0 +1,143 @@
|
||||
-- =====================================================
|
||||
-- 分享免单通知消息 - 数据库执行语句
|
||||
-- =====================================================
|
||||
|
||||
-- 1. 添加分享免单相关的通知类型说明
|
||||
-- ml_notifications 表的 type 字段可包含以下类型:
|
||||
-- 'system' - 系统通知
|
||||
-- 'promotion' - 优惠活动
|
||||
-- 'order' - 订单通知
|
||||
-- 'share_free' - 分享免单通知 (新增)
|
||||
|
||||
-- 2. 创建分享免单通知的函数
|
||||
-- 当分享免单成功时,自动发送通知给用户
|
||||
CREATE OR REPLACE FUNCTION notify_share_free_success(
|
||||
p_user_id UUID,
|
||||
p_share_record_id UUID,
|
||||
p_product_name VARCHAR,
|
||||
p_reward_amount DECIMAL(10,2),
|
||||
p_share_code VARCHAR
|
||||
)
|
||||
RETURNS VOID AS $$
|
||||
BEGIN
|
||||
INSERT INTO ml_notifications (
|
||||
user_id,
|
||||
type,
|
||||
title,
|
||||
content,
|
||||
icon_url,
|
||||
link_url,
|
||||
extra_data,
|
||||
created_at
|
||||
) VALUES (
|
||||
p_user_id,
|
||||
'share_free',
|
||||
'恭喜!您已获得免单奖励',
|
||||
'您分享的商品【' || p_product_name || '】已有4位好友购买,获得免单奖励 ¥' || p_reward_amount || '!分享码:' || p_share_code,
|
||||
'/static/icons/gift.png',
|
||||
'/pages/mall/consumer/share/detail?id=' || p_share_record_id,
|
||||
jsonb_build_object(
|
||||
'share_record_id', p_share_record_id,
|
||||
'product_name', p_product_name,
|
||||
'reward_amount', p_reward_amount,
|
||||
'share_code', p_share_code
|
||||
),
|
||||
NOW()
|
||||
);
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- 3. 创建二级购买进度通知函数
|
||||
-- 当有人通过分享码购买时,发送进度通知
|
||||
CREATE OR REPLACE FUNCTION notify_share_purchase_progress(
|
||||
p_user_id UUID,
|
||||
p_share_record_id UUID,
|
||||
p_product_name VARCHAR,
|
||||
p_current_count INT,
|
||||
p_required_count INT,
|
||||
p_share_code VARCHAR
|
||||
)
|
||||
RETURNS VOID AS $$
|
||||
BEGIN
|
||||
INSERT INTO ml_notifications (
|
||||
user_id,
|
||||
type,
|
||||
title,
|
||||
content,
|
||||
icon_url,
|
||||
link_url,
|
||||
extra_data,
|
||||
created_at
|
||||
) VALUES (
|
||||
p_user_id,
|
||||
'share_free',
|
||||
'分享免单进度更新',
|
||||
'您分享的商品【' || p_product_name || '】已有 ' || p_current_count || '/' || p_required_count || ' 人购买,再邀请 ' || (p_required_count - p_current_count) || ' 人即可免单!',
|
||||
'/static/icons/share.png',
|
||||
'/pages/mall/consumer/share/detail?id=' || p_share_record_id,
|
||||
jsonb_build_object(
|
||||
'share_record_id', p_share_record_id,
|
||||
'product_name', p_product_name,
|
||||
'current_count', p_current_count,
|
||||
'required_count', p_required_count,
|
||||
'share_code', p_share_code
|
||||
),
|
||||
NOW()
|
||||
);
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- 4. 创建分享创建成功通知函数
|
||||
-- 当用户创建分享时,发送确认通知
|
||||
CREATE OR REPLACE FUNCTION notify_share_created(
|
||||
p_user_id UUID,
|
||||
p_share_record_id UUID,
|
||||
p_product_name VARCHAR,
|
||||
p_share_code VARCHAR
|
||||
)
|
||||
RETURNS VOID AS $$
|
||||
BEGIN
|
||||
INSERT INTO ml_notifications (
|
||||
user_id,
|
||||
type,
|
||||
title,
|
||||
content,
|
||||
icon_url,
|
||||
link_url,
|
||||
extra_data,
|
||||
created_at
|
||||
) VALUES (
|
||||
p_user_id,
|
||||
'share_free',
|
||||
'分享创建成功',
|
||||
'您已成功分享商品【' || p_product_name || '】,分享码:' || p_share_code || '。邀请4位好友购买即可免单!',
|
||||
'/static/icons/share-success.png',
|
||||
'/pages/mall/consumer/share/detail?id=' || p_share_record_id,
|
||||
jsonb_build_object(
|
||||
'share_record_id', p_share_record_id,
|
||||
'product_name', p_product_name,
|
||||
'share_code', p_share_code
|
||||
),
|
||||
NOW()
|
||||
);
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- 5. 示例:手动插入测试通知(可选)
|
||||
-- 替换 'YOUR_USER_ID' 为实际用户ID
|
||||
-- INSERT INTO ml_notifications (user_id, type, title, content, icon_url, link_url, extra_data, created_at)
|
||||
-- VALUES (
|
||||
-- 'YOUR_USER_ID',
|
||||
-- 'share_free',
|
||||
-- '恭喜!您已获得免单奖励',
|
||||
-- '您分享的商品【测试商品】已有4位好友购买,获得免单奖励 ¥99.00!分享码:ABCD1234',
|
||||
-- '/static/icons/gift.png',
|
||||
-- '/pages/mall/consumer/share/detail?id=test-share-id',
|
||||
-- '{"share_record_id": "test-share-id", "product_name": "测试商品", "reward_amount": 99.00, "share_code": "ABCD1234"}',
|
||||
-- NOW()
|
||||
-- );
|
||||
|
||||
-- 6. 查询用户分享免单相关通知
|
||||
-- SELECT * FROM ml_notifications
|
||||
-- WHERE type = 'share_free'
|
||||
-- ORDER BY created_at DESC;
|
||||
Reference in New Issue
Block a user