-- ===================================================================================== -- 数据分析模块(正式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; $$;