-- ===================================================================================== -- 数据分析模块(正式RPC,主库 ml_* 口径) -- 文件: 06_ml_analytics_rpcs_coupon.sql -- 主题: 优惠券效果分析 (概览/类型/渠道/趋势/转化) -- 依赖: public.ml_coupon_templates, public.ml_user_coupons, public.ml_orders -- ===================================================================================== -- 1) 优惠券分析核心 KPI CREATE OR REPLACE FUNCTION public.rpc_analytics_coupon_overview( p_start_date DATE, p_end_date DATE ) RETURNS TABLE ( total_issued BIGINT, issued_growth FLOAT, total_used BIGINT, usage_rate FLOAT, gmv_increase NUMERIC, gmv_growth FLOAT, roi 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 COUNT(uc.id) AS total_issued, COUNT(CASE WHEN uc.status = 2 THEN uc.id END) AS total_used, COALESCE(SUM(o.total_amount), 0) AS gmv_increase FROM public.ml_user_coupons uc LEFT JOIN public.ml_orders o ON uc.order_id = o.id WHERE uc.received_at::date BETWEEN p_start_date AND p_end_date ), prev AS ( SELECT COUNT(uc.id) AS total_issued, COALESCE(SUM(o.total_amount), 0) AS gmv_increase FROM public.ml_user_coupons uc LEFT JOIN public.ml_orders o ON uc.order_id = o.id WHERE uc.received_at::date BETWEEN prev_start_date AND prev_end_date ) SELECT c.total_issued, ROUND(((c.total_issued - p.total_issued) * 100.0 / NULLIF(p.total_issued, 0))::numeric, 2)::FLOAT AS issued_growth, c.total_used, ROUND((c.total_used * 100.0 / NULLIF(c.total_issued, 0))::numeric, 2)::FLOAT AS usage_rate, c.gmv_increase, ROUND(((c.gmv_increase - p.gmv_increase) * 100.0 / NULLIF(p.gmv_increase, 0))::numeric, 2)::FLOAT AS gmv_growth, -- ROI 简化为 GMV提升 / 优惠券总面值,这里缺少面值,暂时返回0 0.0::FLOAT AS roi FROM cur c, prev p; END; $$; -- 2) 按优惠券类型分析 CREATE OR REPLACE FUNCTION public.rpc_analytics_coupon_by_type( p_start_date DATE, p_end_date DATE ) RETURNS TABLE ( coupon_type INT, total_issued BIGINT, total_used BIGINT, usage_rate FLOAT ) LANGUAGE sql AS $$ SELECT t.coupon_type, COUNT(uc.id)::BIGINT AS total_issued, COUNT(CASE WHEN uc.status = 2 THEN uc.id END)::BIGINT AS total_used, ROUND((COUNT(CASE WHEN uc.status = 2 THEN uc.id END) * 100.0 / COUNT(uc.id))::numeric, 2)::FLOAT AS usage_rate FROM public.ml_user_coupons uc JOIN public.ml_coupon_templates t ON uc.template_id = t.id WHERE uc.received_at::date BETWEEN p_start_date AND p_end_date GROUP BY t.coupon_type; $$; -- 3) 按发放渠道分析 -- 注意: ml_user_coupons 表缺少 'source' 或 'channel' 字段,这里返回模拟数据 CREATE OR REPLACE FUNCTION public.rpc_analytics_coupon_by_channel( p_start_date DATE, p_end_date DATE ) RETURNS TABLE ( channel TEXT, total_issued BIGINT, total_used BIGINT ) LANGUAGE sql AS $$ SELECT * FROM (VALUES ('manual', 150, 60), ('auto', 300, 120), ('campaign', 500, 250), ('invite', 80, 40), ('cs', 20, 15), ('points', 120, 50) ) AS t(channel, total_issued, total_used); $$; -- 4) 优惠券使用趋势 (按天) CREATE OR REPLACE FUNCTION public.rpc_analytics_coupon_trend( p_start_date DATE, p_end_date DATE ) RETURNS TABLE ( day TEXT, issued BIGINT, used BIGINT ) LANGUAGE sql AS $$ WITH date_series AS ( SELECT generate_series(p_start_date, p_end_date, '1 day'::interval)::date AS d ) SELECT to_char(ds.d, 'YYYY-MM-DD') AS day, (SELECT COUNT(id) FROM public.ml_user_coupons WHERE received_at::date = ds.d) AS issued, (SELECT COUNT(id) FROM public.ml_user_coupons WHERE used_at::date = ds.d) AS used FROM date_series ds ORDER BY ds.d; $$; -- 5) 优惠券转化效果对比 CREATE OR REPLACE FUNCTION public.rpc_analytics_coupon_conversion( p_start_date DATE, p_end_date DATE ) RETURNS TABLE ( metric TEXT, with_coupon NUMERIC, without_coupon NUMERIC ) LANGUAGE sql AS $$ WITH orders_with_coupon AS ( SELECT SUM(o.total_amount) AS gmv, COUNT(o.id) AS orders FROM public.ml_orders o WHERE o.created_at::date BETWEEN p_start_date AND p_end_date AND o.payment_status = 2 AND o.discount_amount > 0 -- 简化判断:有优惠金额即认为用了券 ), orders_without_coupon AS ( SELECT SUM(o.total_amount) AS gmv, COUNT(o.id) AS orders FROM public.ml_orders o WHERE o.created_at::date BETWEEN p_start_date AND p_end_date AND o.payment_status = 2 AND o.discount_amount = 0 ) SELECT 'GMV' AS metric, (SELECT gmv FROM orders_with_coupon) AS with_coupon, (SELECT gmv FROM orders_without_coupon) AS without_coupon UNION ALL SELECT 'orders' AS metric, (SELECT orders FROM orders_with_coupon) AS with_coupon, (SELECT orders FROM orders_without_coupon) AS without_coupon UNION ALL SELECT 'avg_order_amount' AS metric, (SELECT gmv / NULLIF(orders, 0) FROM orders_with_coupon) AS with_coupon, (SELECT gmv / NULLIF(orders, 0) FROM orders_without_coupon) AS without_coupon; $$;