mall数据库文件
This commit is contained in:
405
pages/mall/analytics/test/07_custom_report_rpcs.sql
Normal file
405
pages/mall/analytics/test/07_custom_report_rpcs.sql
Normal file
@@ -0,0 +1,405 @@
|
||||
-- ============================================
|
||||
-- 07_custom_report_rpcs.sql
|
||||
-- 自定义报表创建与管理 RPC 定义
|
||||
-- ============================================
|
||||
-- 目标:
|
||||
-- 1) 为 `pages/mall/analytics/custom-report.uvue` 提供安全的数据服务
|
||||
-- 2) 确保用户记录存在,解决外键约束问题
|
||||
-- 3) 创建自定义报表并生成初始数据(metrics + rows)
|
||||
--
|
||||
-- 依赖前置脚本:
|
||||
-- - 01_create_tables.sql(users 表)
|
||||
-- - ANALYTICS_DB_SCHEMA.sql(analytics_reports / analytics_report_metrics / analytics_report_rows)
|
||||
--
|
||||
-- 使用说明:
|
||||
-- - 前端通过 supabase-js / UTS 调用 `rpc()` 访问本文件中的函数
|
||||
-- - 所有函数仅对 `authenticated` 角色开放执行权限
|
||||
-- ============================================
|
||||
|
||||
-- --------------------------------------------
|
||||
-- 1. 确保用户记录存在(Upsert User)
|
||||
-- --------------------------------------------
|
||||
-- 说明:
|
||||
-- - 如果 users 表中不存在当前用户记录,则插入
|
||||
-- - 如果已存在,则更新最后登录时间等信息
|
||||
-- - 解决 analytics_reports.owner_user_id 外键约束问题
|
||||
CREATE OR REPLACE FUNCTION public.rpc_ensure_user_record(
|
||||
p_user_id uuid,
|
||||
p_email text DEFAULT NULL,
|
||||
p_phone text DEFAULT NULL,
|
||||
p_nickname text DEFAULT NULL
|
||||
)
|
||||
RETURNS uuid
|
||||
LANGUAGE plpgsql
|
||||
SECURITY DEFINER
|
||||
SET search_path = public
|
||||
AS $$
|
||||
DECLARE
|
||||
v_user_id uuid;
|
||||
BEGIN
|
||||
-- 检查用户是否存在
|
||||
SELECT id INTO v_user_id
|
||||
FROM public.users
|
||||
WHERE id = p_user_id;
|
||||
|
||||
IF v_user_id IS NULL THEN
|
||||
-- 用户不存在,插入新记录
|
||||
INSERT INTO public.users (
|
||||
id,
|
||||
email,
|
||||
phone,
|
||||
nickname,
|
||||
last_login_at,
|
||||
created_at,
|
||||
updated_at
|
||||
) VALUES (
|
||||
p_user_id,
|
||||
p_email,
|
||||
p_phone,
|
||||
COALESCE(p_nickname, COALESCE(split_part(p_email, '@', 1), '用户')),
|
||||
NOW(),
|
||||
NOW(),
|
||||
NOW()
|
||||
)
|
||||
RETURNING id INTO v_user_id;
|
||||
ELSE
|
||||
-- 用户已存在,更新信息
|
||||
UPDATE public.users
|
||||
SET
|
||||
email = COALESCE(p_email, email),
|
||||
phone = COALESCE(p_phone, phone),
|
||||
nickname = COALESCE(p_nickname, nickname),
|
||||
last_login_at = NOW(),
|
||||
updated_at = NOW()
|
||||
WHERE id = p_user_id;
|
||||
|
||||
v_user_id := p_user_id;
|
||||
END IF;
|
||||
|
||||
RETURN v_user_id;
|
||||
END;
|
||||
$$;
|
||||
|
||||
REVOKE ALL ON FUNCTION public.rpc_ensure_user_record(uuid,text,text,text) FROM PUBLIC;
|
||||
GRANT EXECUTE ON FUNCTION public.rpc_ensure_user_record(uuid,text,text,text) TO authenticated;
|
||||
|
||||
-- --------------------------------------------
|
||||
-- 2. 创建自定义报表(含初始数据生成)
|
||||
-- --------------------------------------------
|
||||
-- 说明:
|
||||
-- - 创建 analytics_reports 记录
|
||||
-- - 根据 period 和选中的指标,生成 analytics_report_metrics
|
||||
-- - 根据 period 聚合 orders 数据,生成 analytics_report_rows
|
||||
CREATE OR REPLACE FUNCTION public.rpc_create_custom_report(
|
||||
p_title text,
|
||||
p_description text DEFAULT '',
|
||||
p_period text DEFAULT '30d', -- 7d/30d/90d/1y
|
||||
p_metrics text[] DEFAULT ARRAY['gmv', 'orders', 'users'], -- 选中的指标列表
|
||||
p_chart_type text DEFAULT 'line' -- 图表类型(暂不存储,仅用于后续扩展)
|
||||
)
|
||||
RETURNS uuid
|
||||
LANGUAGE plpgsql
|
||||
SECURITY DEFINER
|
||||
SET search_path = public
|
||||
AS $$
|
||||
DECLARE
|
||||
v_user_id uuid;
|
||||
v_report_id uuid;
|
||||
v_start_date date;
|
||||
v_end_date date;
|
||||
v_metric_key text;
|
||||
v_metric_label text;
|
||||
v_metric_value numeric;
|
||||
v_total_gmv numeric := 0;
|
||||
v_total_orders integer := 0;
|
||||
v_total_users integer := 0;
|
||||
v_avg_order_amount numeric := 0;
|
||||
BEGIN
|
||||
-- 1. 获取当前用户 ID
|
||||
v_user_id := auth.uid();
|
||||
IF v_user_id IS NULL THEN
|
||||
RAISE EXCEPTION '用户未登录';
|
||||
END IF;
|
||||
|
||||
-- 2. 确保用户记录存在
|
||||
PERFORM public.rpc_ensure_user_record(
|
||||
v_user_id,
|
||||
NULL, -- email 从 auth.users 获取,这里不传
|
||||
NULL, -- phone 从 auth.users 获取,这里不传
|
||||
NULL -- nickname 从 auth.users 获取,这里不传
|
||||
);
|
||||
|
||||
-- 3. 计算时间范围
|
||||
v_end_date := CURRENT_DATE;
|
||||
CASE p_period
|
||||
WHEN '7d' THEN v_start_date := v_end_date - INTERVAL '7 days';
|
||||
WHEN '30d' THEN v_start_date := v_end_date - INTERVAL '30 days';
|
||||
WHEN '90d' THEN v_start_date := v_end_date - INTERVAL '90 days';
|
||||
WHEN '1y' THEN v_start_date := v_end_date - INTERVAL '1 year';
|
||||
ELSE v_start_date := v_end_date - INTERVAL '30 days'; -- 默认 30 天
|
||||
END CASE;
|
||||
|
||||
-- 4. 创建报表记录
|
||||
INSERT INTO public.analytics_reports (
|
||||
owner_user_id,
|
||||
title,
|
||||
description,
|
||||
type,
|
||||
period,
|
||||
date_start,
|
||||
date_end,
|
||||
status,
|
||||
generated_at,
|
||||
created_at,
|
||||
updated_at
|
||||
) VALUES (
|
||||
v_user_id,
|
||||
p_title,
|
||||
p_description,
|
||||
'custom',
|
||||
p_period,
|
||||
v_start_date,
|
||||
v_end_date,
|
||||
'ready',
|
||||
NOW(),
|
||||
NOW(),
|
||||
NOW()
|
||||
)
|
||||
RETURNING id INTO v_report_id;
|
||||
|
||||
-- 5. 聚合订单数据,计算总指标
|
||||
SELECT
|
||||
COALESCE(SUM(o.total_amount), 0),
|
||||
COUNT(DISTINCT o.id),
|
||||
COUNT(DISTINCT o.user_id),
|
||||
CASE
|
||||
WHEN COUNT(DISTINCT o.id) > 0
|
||||
THEN COALESCE(SUM(o.total_amount), 0) / COUNT(DISTINCT o.id)
|
||||
ELSE 0
|
||||
END
|
||||
INTO v_total_gmv, v_total_orders, v_total_users, v_avg_order_amount
|
||||
FROM public.orders o
|
||||
WHERE o.created_at >= v_start_date
|
||||
AND o.created_at <= v_end_date
|
||||
AND o.status = 2; -- 已完成订单
|
||||
|
||||
-- 6. 生成核心指标(analytics_report_metrics)
|
||||
-- GMV
|
||||
IF 'gmv' = ANY(p_metrics) THEN
|
||||
INSERT INTO public.analytics_report_metrics (
|
||||
report_id,
|
||||
metric_key,
|
||||
metric_label,
|
||||
metric_value_num,
|
||||
format,
|
||||
icon,
|
||||
color
|
||||
) VALUES (
|
||||
v_report_id,
|
||||
'gmv_total',
|
||||
'总GMV',
|
||||
v_total_gmv,
|
||||
'currency',
|
||||
'💰',
|
||||
'#4caf50'
|
||||
);
|
||||
END IF;
|
||||
|
||||
-- 订单数
|
||||
IF 'orders' = ANY(p_metrics) THEN
|
||||
INSERT INTO public.analytics_report_metrics (
|
||||
report_id,
|
||||
metric_key,
|
||||
metric_label,
|
||||
metric_value_num,
|
||||
format,
|
||||
icon,
|
||||
color
|
||||
) VALUES (
|
||||
v_report_id,
|
||||
'orders_total',
|
||||
'总订单数',
|
||||
v_total_orders,
|
||||
'number',
|
||||
'📦',
|
||||
'#2196f3'
|
||||
);
|
||||
END IF;
|
||||
|
||||
-- 用户数
|
||||
IF 'users' = ANY(p_metrics) THEN
|
||||
INSERT INTO public.analytics_report_metrics (
|
||||
report_id,
|
||||
metric_key,
|
||||
metric_label,
|
||||
metric_value_num,
|
||||
format,
|
||||
icon,
|
||||
color
|
||||
) VALUES (
|
||||
v_report_id,
|
||||
'users_total',
|
||||
'下单用户数',
|
||||
v_total_users,
|
||||
'number',
|
||||
'👥',
|
||||
'#ff9800'
|
||||
);
|
||||
END IF;
|
||||
|
||||
-- 客单价
|
||||
IF 'avg_order' = ANY(p_metrics) THEN
|
||||
INSERT INTO public.analytics_report_metrics (
|
||||
report_id,
|
||||
metric_key,
|
||||
metric_label,
|
||||
metric_value_num,
|
||||
format,
|
||||
icon,
|
||||
color
|
||||
) VALUES (
|
||||
v_report_id,
|
||||
'avg_order_amount',
|
||||
'客单价',
|
||||
v_avg_order_amount,
|
||||
'currency',
|
||||
'💵',
|
||||
'#9c27b0'
|
||||
);
|
||||
END IF;
|
||||
|
||||
-- 7. 生成明细行(analytics_report_rows)- 按天聚合
|
||||
INSERT INTO public.analytics_report_rows (
|
||||
report_id,
|
||||
row_date,
|
||||
gmv,
|
||||
orders,
|
||||
users,
|
||||
avg_order_amount
|
||||
)
|
||||
SELECT
|
||||
v_report_id,
|
||||
o.created_at::date AS row_date,
|
||||
COALESCE(SUM(o.total_amount), 0) AS gmv,
|
||||
COUNT(DISTINCT o.id) AS orders,
|
||||
COUNT(DISTINCT o.user_id) AS users,
|
||||
CASE
|
||||
WHEN COUNT(DISTINCT o.id) > 0
|
||||
THEN COALESCE(SUM(o.total_amount), 0) / COUNT(DISTINCT o.id)
|
||||
ELSE 0
|
||||
END AS avg_order_amount
|
||||
FROM public.orders o
|
||||
WHERE o.created_at >= v_start_date
|
||||
AND o.created_at <= v_end_date
|
||||
AND o.status = 2
|
||||
GROUP BY o.created_at::date
|
||||
ORDER BY o.created_at::date;
|
||||
|
||||
RETURN v_report_id;
|
||||
END;
|
||||
$$;
|
||||
|
||||
REVOKE ALL ON FUNCTION public.rpc_create_custom_report(text,text,text,text[],text) FROM PUBLIC;
|
||||
GRANT EXECUTE ON FUNCTION public.rpc_create_custom_report(text,text,text,text[],text) TO authenticated;
|
||||
|
||||
-- --------------------------------------------
|
||||
-- 3. 更新自定义报表(仅更新基本信息)
|
||||
-- --------------------------------------------
|
||||
-- 说明:
|
||||
-- - 更新报表的标题、描述、周期
|
||||
-- - 不重新生成数据(如需重新生成,删除后重建)
|
||||
CREATE OR REPLACE FUNCTION public.rpc_update_custom_report(
|
||||
p_report_id uuid,
|
||||
p_title text,
|
||||
p_description text DEFAULT NULL,
|
||||
p_period text DEFAULT NULL
|
||||
)
|
||||
RETURNS boolean
|
||||
LANGUAGE plpgsql
|
||||
SECURITY DEFINER
|
||||
SET search_path = public
|
||||
AS $$
|
||||
DECLARE
|
||||
v_user_id uuid;
|
||||
BEGIN
|
||||
-- 1. 获取当前用户 ID
|
||||
v_user_id := auth.uid();
|
||||
IF v_user_id IS NULL THEN
|
||||
RAISE EXCEPTION '用户未登录';
|
||||
END IF;
|
||||
|
||||
-- 2. 更新报表(仅限所有者)
|
||||
UPDATE public.analytics_reports
|
||||
SET
|
||||
title = p_title,
|
||||
description = COALESCE(p_description, description),
|
||||
period = COALESCE(p_period, period),
|
||||
updated_at = NOW()
|
||||
WHERE id = p_report_id
|
||||
AND owner_user_id = v_user_id;
|
||||
|
||||
-- FOUND 是 PostgreSQL 的特殊变量,UPDATE 后自动设置
|
||||
IF NOT FOUND THEN
|
||||
RAISE EXCEPTION '报表不存在或无权限修改';
|
||||
END IF;
|
||||
|
||||
RETURN true;
|
||||
END;
|
||||
$$;
|
||||
|
||||
REVOKE ALL ON FUNCTION public.rpc_update_custom_report(uuid,text,text,text) FROM PUBLIC;
|
||||
GRANT EXECUTE ON FUNCTION public.rpc_update_custom_report(uuid,text,text,text) TO authenticated;
|
||||
|
||||
-- --------------------------------------------
|
||||
-- 4. 删除自定义报表(级联删除相关数据)
|
||||
-- --------------------------------------------
|
||||
-- 说明:
|
||||
-- - 删除报表记录(CASCADE 会自动删除 metrics 和 rows)
|
||||
-- - 仅允许所有者删除
|
||||
CREATE OR REPLACE FUNCTION public.rpc_delete_custom_report(
|
||||
p_report_id uuid
|
||||
)
|
||||
RETURNS boolean
|
||||
LANGUAGE plpgsql
|
||||
SECURITY DEFINER
|
||||
SET search_path = public
|
||||
AS $$
|
||||
DECLARE
|
||||
v_user_id uuid;
|
||||
BEGIN
|
||||
-- 1. 获取当前用户 ID
|
||||
v_user_id := auth.uid();
|
||||
IF v_user_id IS NULL THEN
|
||||
RAISE EXCEPTION '用户未登录';
|
||||
END IF;
|
||||
|
||||
-- 2. 删除报表(仅限所有者,CASCADE 会自动删除 metrics 和 rows)
|
||||
DELETE FROM public.analytics_reports
|
||||
WHERE id = p_report_id
|
||||
AND owner_user_id = v_user_id
|
||||
AND type = 'custom';
|
||||
|
||||
-- FOUND 是 PostgreSQL 的特殊变量,DELETE 后自动设置
|
||||
IF NOT FOUND THEN
|
||||
RAISE EXCEPTION '报表不存在或无权限删除';
|
||||
END IF;
|
||||
|
||||
RETURN true;
|
||||
END;
|
||||
$$;
|
||||
|
||||
REVOKE ALL ON FUNCTION public.rpc_delete_custom_report(uuid) FROM PUBLIC;
|
||||
GRANT EXECUTE ON FUNCTION public.rpc_delete_custom_report(uuid) TO authenticated;
|
||||
|
||||
-- ============================================
|
||||
-- 完成提示
|
||||
-- ============================================
|
||||
DO $$
|
||||
BEGIN
|
||||
RAISE NOTICE 'Custom report RPCs created successfully.';
|
||||
RAISE NOTICE 'Functions:';
|
||||
RAISE NOTICE ' - rpc_ensure_user_record(uuid, text, text, text)';
|
||||
RAISE NOTICE ' - rpc_create_custom_report(text, text, text, text[], text)';
|
||||
RAISE NOTICE ' - rpc_update_custom_report(uuid, text, text, text)';
|
||||
RAISE NOTICE ' - rpc_delete_custom_report(uuid)';
|
||||
END $$;
|
||||
Reference in New Issue
Block a user