Files
medical-mall/pages/mall/analytics/test/02_ml_analytics_rpcs_sales.sql
2026-01-31 21:47:42 +08:00

177 lines
6.1 KiB
PL/PgSQL
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
-- =====================================================================================
-- 数据分析模块正式RPC主库 ml_* 口径)
-- 文件: 02_ml_analytics_rpcs_sales.sql
-- 主题: 仪表盘/销售报表KPI/趋势/TOP
-- 口径约定:
-- - GMV: paid_amount 汇总(若为 0 则用 total_amount 兜底)
-- - 订单量: ml_orders created_at 期间内订单数(可按需要切换为支付订单数)
-- - 支付用户数: payment_status=2 的 distinct user_id
-- - 活跃用户数: ml_browse_history created_at 期间内 distinct user_id弱口径
-- - 转化率(A): 支付用户数 / 活跃用户数(*100
-- =====================================================================================
-- 1) 销售核心 KPI含上期对比
CREATE OR REPLACE FUNCTION public.rpc_analytics_sales_kpis(
p_start_date DATE,
p_end_date DATE
)
RETURNS TABLE (
gmv NUMERIC,
gmv_growth FLOAT,
orders BIGINT,
order_growth FLOAT,
conversion_rate FLOAT,
conversion_growth FLOAT,
avg_order_amount NUMERIC,
avg_order_growth FLOAT
)
LANGUAGE plpgsql
AS $$
DECLARE
prev_start_date DATE;
prev_end_date DATE;
period_days INT;
BEGIN
period_days := p_end_date - p_start_date + 1;
prev_start_date := p_start_date - period_days;
prev_end_date := p_start_date - 1;
RETURN QUERY
WITH
-- 当前周期
cur AS (
SELECT
COALESCE(
SUM(CASE WHEN o.payment_status = 2 THEN COALESCE(NULLIF(o.paid_amount, 0), o.total_amount) ELSE 0 END),
0
) AS gmv,
COUNT(*)::BIGINT AS orders,
COUNT(DISTINCT CASE WHEN o.payment_status = 2 THEN o.user_id END)::BIGINT AS paid_users,
(SELECT COUNT(DISTINCT bh.user_id) FROM public.ml_browse_history bh WHERE bh.created_at::DATE BETWEEN p_start_date AND p_end_date)::BIGINT AS active_users
FROM public.ml_orders o
WHERE o.created_at::DATE BETWEEN p_start_date AND p_end_date
),
-- 上一周期
prev AS (
SELECT
COALESCE(
SUM(CASE WHEN o.payment_status = 2 THEN COALESCE(NULLIF(o.paid_amount, 0), o.total_amount) ELSE 0 END),
0
) AS gmv,
COUNT(*)::BIGINT AS orders,
COUNT(DISTINCT CASE WHEN o.payment_status = 2 THEN o.user_id END)::BIGINT AS paid_users,
(SELECT COUNT(DISTINCT bh.user_id) FROM public.ml_browse_history bh WHERE bh.created_at::DATE BETWEEN prev_start_date AND prev_end_date)::BIGINT AS active_users
FROM public.ml_orders o
WHERE o.created_at::DATE BETWEEN prev_start_date AND prev_end_date
),
calc AS (
SELECT
cur.gmv AS gmv,
cur.orders AS orders,
CASE WHEN cur.active_users > 0 THEN (cur.paid_users::NUMERIC / cur.active_users::NUMERIC) * 100 ELSE 0 END AS conversion_rate,
CASE WHEN cur.orders > 0 THEN cur.gmv / cur.orders ELSE 0 END AS avg_order_amount,
prev.gmv AS prev_gmv,
prev.orders AS prev_orders,
CASE WHEN prev.active_users > 0 THEN (prev.paid_users::NUMERIC / prev.active_users::NUMERIC) * 100 ELSE 0 END AS prev_conversion_rate,
CASE WHEN prev.orders > 0 THEN prev.gmv / prev.orders ELSE 0 END AS prev_avg_order_amount
FROM cur, prev
)
SELECT
ROUND(calc.gmv, 2) AS gmv,
ROUND(((calc.gmv - calc.prev_gmv) * 100.0 / NULLIF(calc.prev_gmv, 0))::numeric, 2)::FLOAT AS gmv_growth,
calc.orders,
ROUND(((calc.orders - calc.prev_orders) * 100.0 / NULLIF(calc.prev_orders, 0))::numeric, 2)::FLOAT AS order_growth,
ROUND(calc.conversion_rate::numeric, 2)::FLOAT AS conversion_rate,
ROUND((calc.conversion_rate - calc.prev_conversion_rate)::numeric, 2)::FLOAT AS conversion_growth,
ROUND(calc.avg_order_amount, 2) AS avg_order_amount,
ROUND(((calc.avg_order_amount - calc.prev_avg_order_amount) * 100.0 / NULLIF(calc.prev_avg_order_amount, 0))::numeric, 2)::FLOAT AS avg_order_growth
FROM calc;
END;
$$;
-- 2) 销售趋势日维度GMV + 订单数)
CREATE OR REPLACE FUNCTION public.rpc_analytics_sales_trend(
p_start_date DATE,
p_end_date DATE
)
RETURNS TABLE (
date DATE,
gmv NUMERIC,
orders BIGINT
)
LANGUAGE sql
AS $$
WITH date_series AS (
SELECT generate_series(p_start_date, p_end_date, '1 day'::interval)::DATE AS date
)
SELECT
ds.date,
COALESCE(
SUM(CASE WHEN o.payment_status = 2 THEN COALESCE(NULLIF(o.paid_amount, 0), o.total_amount) ELSE 0 END),
0
) AS gmv,
COUNT(o.id)::BIGINT AS orders
FROM date_series ds
LEFT JOIN public.ml_orders o
ON o.created_at::DATE = ds.date
GROUP BY ds.date
ORDER BY ds.date;
$$;
-- 3) 热销商品 TOP按销量sum(quantity)
CREATE OR REPLACE FUNCTION public.rpc_analytics_top_products(
p_start_date DATE,
p_end_date DATE,
p_limit INT DEFAULT 50
)
RETURNS TABLE (
id UUID,
name TEXT,
sales BIGINT
)
LANGUAGE sql
AS $$
SELECT
p.id,
p.name::TEXT,
COALESCE(SUM(oi.quantity), 0)::BIGINT AS sales
FROM public.ml_order_items oi
JOIN public.ml_orders o ON o.id = oi.order_id
JOIN public.ml_products p ON p.id = oi.product_id
WHERE o.created_at::DATE BETWEEN p_start_date AND p_end_date
AND o.payment_status = 2
GROUP BY p.id, p.name
ORDER BY sales DESC
LIMIT p_limit;
$$;
-- 4) 商家排行 TOP按 GMV支付 GMV
CREATE OR REPLACE FUNCTION public.rpc_analytics_top_merchants(
p_start_date DATE,
p_end_date DATE,
p_limit INT DEFAULT 50
)
RETURNS TABLE (
id UUID,
name TEXT,
sales NUMERIC
)
LANGUAGE sql
AS $$
SELECT
m.id,
COALESCE(NULLIF(m.username, ''), '未知商家')::TEXT AS name,
COALESCE(SUM(COALESCE(NULLIF(o.paid_amount, 0), o.total_amount)), 0) AS sales
FROM public.ml_orders o
JOIN public.ak_users m ON m.id = o.merchant_id
WHERE o.created_at::DATE BETWEEN p_start_date AND p_end_date
AND o.payment_status = 2
GROUP BY m.id, m.username
ORDER BY sales DESC
LIMIT p_limit;
$$;