343 lines
8.9 KiB
PL/PgSQL
343 lines
8.9 KiB
PL/PgSQL
-- ============================================
|
||
-- DATA_DETAIL_RPCS.sql
|
||
-- 数据分析详情页专用 RPC 定义
|
||
-- ============================================
|
||
-- 目标:
|
||
-- 1) 为 `pages/mall/analytics/data-detail.uvue` 提供统一的数据服务
|
||
-- 2) 仅复用现有 analytics_* 表与业务表,不新增物理表
|
||
-- 3) 权限完全依赖各表自身的 RLS 策略,本文件只负责函数与 GRANT
|
||
--
|
||
-- 依赖前置脚本:
|
||
-- - 01_create_tables.sql
|
||
-- - ../../user/test/USER_AUTH_SCHEMA.sql
|
||
-- - ../../user/test/USER_AUTH_TRIGGER.sql
|
||
-- - ANALYTICS_DB_SCHEMA.sql
|
||
--
|
||
-- 使用说明:
|
||
-- - 前端通过 supabase-js / UTS 调用 `rpc()` 访问本文件中的函数
|
||
-- - 所有函数仅对 `authenticated` 角色开放执行权限
|
||
-- ============================================
|
||
|
||
-- --------------------------------------------
|
||
-- 1. 报表基础信息(用于初始化筛选器)
|
||
-- --------------------------------------------
|
||
-- 根据报表 ID 返回基础配置,包含标题、类型、时间范围等
|
||
CREATE OR REPLACE FUNCTION public.rpc_data_detail_report_info(
|
||
p_report_id uuid
|
||
)
|
||
RETURNS TABLE (
|
||
id uuid,
|
||
title text,
|
||
type text,
|
||
period text,
|
||
date_start date,
|
||
date_end date,
|
||
status text,
|
||
merchant_id uuid
|
||
)
|
||
LANGUAGE sql
|
||
AS $$
|
||
SELECT
|
||
r.id,
|
||
r.title,
|
||
r.type,
|
||
r.period,
|
||
r.date_start,
|
||
r.date_end,
|
||
r.status,
|
||
r.merchant_id
|
||
FROM public.analytics_reports r
|
||
WHERE r.id = p_report_id
|
||
$$;
|
||
|
||
REVOKE ALL ON FUNCTION public.rpc_data_detail_report_info(uuid) FROM PUBLIC;
|
||
GRANT EXECUTE ON FUNCTION public.rpc_data_detail_report_info(uuid) TO authenticated;
|
||
|
||
|
||
-- --------------------------------------------
|
||
-- 2. 报表明细行(表格数据)
|
||
-- --------------------------------------------
|
||
-- 说明:
|
||
-- - 以 analytics_report_rows 作为数据源
|
||
-- - 可按日期 / GMV / 订单数 / 用户数排序
|
||
-- - 维度信息通过 extra(JSONB) 透出,前端可自由解析
|
||
CREATE OR REPLACE FUNCTION public.rpc_data_detail_rows(
|
||
p_report_id uuid,
|
||
p_sort_by text DEFAULT 'row_date', -- row_date | gmv | orders | users
|
||
p_sort_dir text DEFAULT 'asc', -- asc | desc
|
||
p_limit integer DEFAULT 200,
|
||
p_offset integer DEFAULT 0
|
||
)
|
||
RETURNS TABLE (
|
||
row_date date,
|
||
gmv numeric,
|
||
orders integer,
|
||
users integer,
|
||
conversion numeric,
|
||
avg_order_amount numeric,
|
||
extra jsonb
|
||
)
|
||
LANGUAGE plpgsql
|
||
AS $$
|
||
BEGIN
|
||
-- 统一的 LIMIT / OFFSET 处理
|
||
IF lower(p_sort_by) = 'gmv' THEN
|
||
IF lower(p_sort_dir) = 'desc' THEN
|
||
RETURN QUERY
|
||
SELECT
|
||
r.row_date,
|
||
r.gmv,
|
||
r.orders,
|
||
r.users,
|
||
r.conversion,
|
||
r.avg_order_amount,
|
||
r.extra
|
||
FROM public.analytics_report_rows r
|
||
WHERE r.report_id = p_report_id
|
||
ORDER BY r.gmv DESC, r.row_date DESC
|
||
LIMIT GREATEST(p_limit, 0)
|
||
OFFSET GREATEST(p_offset, 0);
|
||
ELSE
|
||
RETURN QUERY
|
||
SELECT
|
||
r.row_date,
|
||
r.gmv,
|
||
r.orders,
|
||
r.users,
|
||
r.conversion,
|
||
r.avg_order_amount,
|
||
r.extra
|
||
FROM public.analytics_report_rows r
|
||
WHERE r.report_id = p_report_id
|
||
ORDER BY r.gmv ASC, r.row_date ASC
|
||
LIMIT GREATEST(p_limit, 0)
|
||
OFFSET GREATEST(p_offset, 0);
|
||
END IF;
|
||
|
||
ELSIF lower(p_sort_by) = 'orders' THEN
|
||
IF lower(p_sort_dir) = 'desc' THEN
|
||
RETURN QUERY
|
||
SELECT
|
||
r.row_date,
|
||
r.gmv,
|
||
r.orders,
|
||
r.users,
|
||
r.conversion,
|
||
r.avg_order_amount,
|
||
r.extra
|
||
FROM public.analytics_report_rows r
|
||
WHERE r.report_id = p_report_id
|
||
ORDER BY r.orders DESC, r.row_date DESC
|
||
LIMIT GREATEST(p_limit, 0)
|
||
OFFSET GREATEST(p_offset, 0);
|
||
ELSE
|
||
RETURN QUERY
|
||
SELECT
|
||
r.row_date,
|
||
r.gmv,
|
||
r.orders,
|
||
r.users,
|
||
r.conversion,
|
||
r.avg_order_amount,
|
||
r.extra
|
||
FROM public.analytics_report_rows r
|
||
WHERE r.report_id = p_report_id
|
||
ORDER BY r.orders ASC, r.row_date ASC
|
||
LIMIT GREATEST(p_limit, 0)
|
||
OFFSET GREATEST(p_offset, 0);
|
||
END IF;
|
||
|
||
ELSIF lower(p_sort_by) = 'users' THEN
|
||
IF lower(p_sort_dir) = 'desc' THEN
|
||
RETURN QUERY
|
||
SELECT
|
||
r.row_date,
|
||
r.gmv,
|
||
r.orders,
|
||
r.users,
|
||
r.conversion,
|
||
r.avg_order_amount,
|
||
r.extra
|
||
FROM public.analytics_report_rows r
|
||
WHERE r.report_id = p_report_id
|
||
ORDER BY r.users DESC, r.row_date DESC
|
||
LIMIT GREATEST(p_limit, 0)
|
||
OFFSET GREATEST(p_offset, 0);
|
||
ELSE
|
||
RETURN QUERY
|
||
SELECT
|
||
r.row_date,
|
||
r.gmv,
|
||
r.orders,
|
||
r.users,
|
||
r.conversion,
|
||
r.avg_order_amount,
|
||
r.extra
|
||
FROM public.analytics_report_rows r
|
||
WHERE r.report_id = p_report_id
|
||
ORDER BY r.users ASC, r.row_date ASC
|
||
LIMIT GREATEST(p_limit, 0)
|
||
OFFSET GREATEST(p_offset, 0);
|
||
END IF;
|
||
|
||
ELSE
|
||
-- 默认按日期排序
|
||
IF lower(p_sort_dir) = 'desc' THEN
|
||
RETURN QUERY
|
||
SELECT
|
||
r.row_date,
|
||
r.gmv,
|
||
r.orders,
|
||
r.users,
|
||
r.conversion,
|
||
r.avg_order_amount,
|
||
r.extra
|
||
FROM public.analytics_report_rows r
|
||
WHERE r.report_id = p_report_id
|
||
ORDER BY r.row_date DESC
|
||
LIMIT GREATEST(p_limit, 0)
|
||
OFFSET GREATEST(p_offset, 0);
|
||
ELSE
|
||
RETURN QUERY
|
||
SELECT
|
||
r.row_date,
|
||
r.gmv,
|
||
r.orders,
|
||
r.users,
|
||
r.conversion,
|
||
r.avg_order_amount,
|
||
r.extra
|
||
FROM public.analytics_report_rows r
|
||
WHERE r.report_id = p_report_id
|
||
ORDER BY r.row_date ASC
|
||
LIMIT GREATEST(p_limit, 0)
|
||
OFFSET GREATEST(p_offset, 0);
|
||
END IF;
|
||
END IF;
|
||
END;
|
||
$$;
|
||
|
||
REVOKE ALL ON FUNCTION public.rpc_data_detail_rows(uuid,text,text,integer,integer) FROM PUBLIC;
|
||
GRANT EXECUTE ON FUNCTION public.rpc_data_detail_rows(uuid,text,text,integer,integer) TO authenticated;
|
||
|
||
|
||
-- --------------------------------------------
|
||
-- 3. 钻取指标列表(KPI / 汇总卡片)
|
||
-- --------------------------------------------
|
||
-- 说明:
|
||
-- - 直接从 analytics_report_metrics 读取
|
||
-- - 前端可根据 format 字段决定展示方式(数字 / 金额 / 百分比)
|
||
CREATE OR REPLACE FUNCTION public.rpc_data_detail_drill_items(
|
||
p_report_id uuid
|
||
)
|
||
RETURNS TABLE (
|
||
metric_key text,
|
||
metric_label text,
|
||
metric_value_num numeric,
|
||
metric_value_text text,
|
||
format text,
|
||
change_pct numeric,
|
||
icon text,
|
||
color text
|
||
)
|
||
LANGUAGE sql
|
||
AS $$
|
||
SELECT
|
||
m.metric_key,
|
||
m.metric_label,
|
||
m.metric_value_num,
|
||
m.metric_value_text,
|
||
m.format,
|
||
m.change_pct,
|
||
m.icon,
|
||
m.color
|
||
FROM public.analytics_report_metrics m
|
||
WHERE m.report_id = p_report_id
|
||
ORDER BY m.metric_key
|
||
$$;
|
||
|
||
REVOKE ALL ON FUNCTION public.rpc_data_detail_drill_items(uuid) FROM PUBLIC;
|
||
GRANT EXECUTE ON FUNCTION public.rpc_data_detail_drill_items(uuid) TO authenticated;
|
||
|
||
|
||
-- --------------------------------------------
|
||
-- 4. GMV 对比数据(当前周期 vs 对比周期)
|
||
-- --------------------------------------------
|
||
-- 说明:
|
||
-- - 当前周期 = analytics_reports.period / date_start/date_end 所定义的范围
|
||
-- - 对比周期 = 与当前周期长度相同的上一段时间
|
||
-- - 聚合来源:orders
|
||
CREATE OR REPLACE FUNCTION public.rpc_data_detail_compare_gmv(
|
||
p_report_id uuid
|
||
)
|
||
RETURNS TABLE (
|
||
day date,
|
||
gmv_current numeric,
|
||
gmv_previous numeric
|
||
)
|
||
LANGUAGE plpgsql
|
||
AS $$
|
||
DECLARE
|
||
v_date_start date;
|
||
v_date_end date;
|
||
v_period_len integer;
|
||
BEGIN
|
||
SELECT
|
||
COALESCE(r.date_start, (now() - INTERVAL '7 days')::date),
|
||
COALESCE(r.date_end, now()::date)
|
||
INTO v_date_start, v_date_end
|
||
FROM public.analytics_reports r
|
||
WHERE r.id = p_report_id;
|
||
|
||
IF v_date_start IS NULL OR v_date_end IS NULL THEN
|
||
RETURN;
|
||
END IF;
|
||
|
||
v_period_len := (v_date_end - v_date_start) + 1;
|
||
|
||
RETURN QUERY
|
||
WITH cur AS (
|
||
SELECT
|
||
o.created_at::date AS day,
|
||
SUM(o.total_amount) AS gmv
|
||
FROM public.orders o
|
||
WHERE o.created_at::date BETWEEN v_date_start AND v_date_end
|
||
AND o.status = 2
|
||
GROUP BY o.created_at::date
|
||
),
|
||
prev_range AS (
|
||
SELECT
|
||
(v_date_start - v_period_len) AS start_date,
|
||
(v_date_start - 1) AS end_date
|
||
),
|
||
prev AS (
|
||
SELECT
|
||
o.created_at::date AS day,
|
||
SUM(o.total_amount) AS gmv
|
||
FROM public.orders o, prev_range pr
|
||
WHERE o.created_at::date BETWEEN pr.start_date AND pr.end_date
|
||
AND o.status = 2
|
||
GROUP BY o.created_at::date
|
||
),
|
||
series AS (
|
||
SELECT generate_series(v_date_start, v_date_end, INTERVAL '1 day')::date AS day
|
||
)
|
||
SELECT
|
||
s.day,
|
||
COALESCE(c.gmv, 0) AS gmv_current,
|
||
COALESCE(p.gmv, 0) AS gmv_previous
|
||
FROM series s
|
||
LEFT JOIN cur c ON c.day = s.day
|
||
LEFT JOIN prev p ON p.day = (s.day - v_period_len);
|
||
END;
|
||
$$;
|
||
|
||
REVOKE ALL ON FUNCTION public.rpc_data_detail_compare_gmv(uuid) FROM PUBLIC;
|
||
GRANT EXECUTE ON FUNCTION public.rpc_data_detail_compare_gmv(uuid) TO authenticated;
|
||
|
||
|
||
-- ============================================
|
||
-- 文件结束
|
||
-- ============================================
|
||
|