-- ===================================================================================== -- 商城分析模块 RPC 函数增量脚本 -- 依赖: ml_* 系列表, ak_users 表 -- 目标: 为数据分析模块提供核心数据接口,优先使用现有表,不新建表。 -- ===================================================================================== -- ===================================================================================== -- 1. 函数: rpc_analytics_user_kpis -- 描述: 获取用户分析核心KPI指标,并与上一周期对比。 -- ===================================================================================== CREATE OR REPLACE FUNCTION public.rpc_analytics_user_kpis( p_start_date DATE, p_end_date DATE ) RETURNS TABLE ( total_users BIGINT, user_growth FLOAT, new_users BIGINT, new_user_growth FLOAT, active_users BIGINT, active_growth FLOAT, ordering_users BIGINT, ordering_growth FLOAT, paid_users BIGINT, paid_growth FLOAT, new_user_conversion_rate FLOAT, repurchase_rate FLOAT, repurchase_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 -- 当前周期数据 current_period AS ( SELECT (SELECT COUNT(*) FROM public.ak_users WHERE created_at <= p_end_date::timestamp) AS total_users, COUNT(DISTINCT CASE WHEN u.created_at::DATE BETWEEN p_start_date AND p_end_date THEN u.id END)::BIGINT AS new_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) AS active_users, (SELECT COUNT(DISTINCT o.user_id) FROM public.ml_orders o WHERE o.created_at::DATE BETWEEN p_start_date AND p_end_date) AS ordering_users, (SELECT COUNT(DISTINCT o.user_id) FROM public.ml_orders o WHERE o.created_at::DATE BETWEEN p_start_date AND p_end_date AND o.payment_status = 2) AS paid_users, ( SELECT COUNT(DISTINCT o.user_id) * 100.0 / NULLIF(COUNT(DISTINCT u_new.id), 0) FROM public.ak_users u_new LEFT JOIN public.ml_orders o ON u_new.id = o.user_id AND o.payment_status = 2 AND o.created_at::DATE BETWEEN p_start_date AND p_end_date WHERE u_new.created_at::DATE BETWEEN p_start_date AND p_end_date ) AS new_user_conversion_rate, ( SELECT COUNT(DISTINCT CASE WHEN user_orders.order_count > 1 THEN user_orders.user_id END) * 100.0 / NULLIF(COUNT(DISTINCT user_orders.user_id), 0) FROM ( SELECT user_id, COUNT(id) as order_count FROM public.ml_orders WHERE created_at::DATE <= p_end_date AND payment_status = 2 GROUP BY user_id ) user_orders WHERE user_orders.user_id IN (SELECT user_id FROM public.ml_orders WHERE created_at::DATE BETWEEN p_start_date AND p_end_date AND payment_status = 2) ) AS repurchase_rate FROM public.ak_users u ), -- 上一周期数据 previous_period AS ( SELECT (SELECT COUNT(*) FROM public.ak_users WHERE created_at <= prev_end_date::timestamp) AS total_users, COUNT(DISTINCT CASE WHEN u.created_at::DATE BETWEEN prev_start_date AND prev_end_date THEN u.id END)::BIGINT AS new_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) AS active_users, (SELECT COUNT(DISTINCT o.user_id) FROM public.ml_orders o WHERE o.created_at::DATE BETWEEN prev_start_date AND prev_end_date) AS ordering_users, (SELECT COUNT(DISTINCT o.user_id) FROM public.ml_orders o WHERE o.created_at::DATE BETWEEN prev_start_date AND prev_end_date AND o.payment_status = 2) AS paid_users, ( SELECT COUNT(DISTINCT CASE WHEN user_orders.order_count > 1 THEN user_orders.user_id END) * 100.0 / NULLIF(COUNT(DISTINCT user_orders.user_id), 0) FROM ( SELECT user_id, COUNT(id) as order_count FROM public.ml_orders WHERE created_at::DATE <= prev_end_date AND payment_status = 2 GROUP BY user_id ) user_orders WHERE user_orders.user_id IN (SELECT user_id FROM public.ml_orders WHERE created_at::DATE BETWEEN prev_start_date AND prev_end_date AND payment_status = 2) ) AS repurchase_rate FROM public.ak_users u ) SELECT cp.total_users, ROUND(((cp.total_users - pp.total_users) * 100.0 / NULLIF(pp.total_users, 0))::numeric, 2)::FLOAT AS user_growth, cp.new_users, ROUND(((cp.new_users - pp.new_users) * 100.0 / NULLIF(pp.new_users, 0))::numeric, 2)::FLOAT AS new_user_growth, cp.active_users, ROUND(((cp.active_users - pp.active_users) * 100.0 / NULLIF(pp.active_users, 0))::numeric, 2)::FLOAT AS active_growth, cp.ordering_users, ROUND(((cp.ordering_users - pp.ordering_users) * 100.0 / NULLIF(pp.ordering_users, 0))::numeric, 2)::FLOAT AS ordering_growth, cp.paid_users, ROUND(((cp.paid_users - pp.paid_users) * 100.0 / NULLIF(pp.paid_users, 0))::numeric, 2)::FLOAT AS paid_growth, ROUND(cp.new_user_conversion_rate::numeric, 2)::FLOAT, ROUND(cp.repurchase_rate::numeric, 2)::FLOAT AS repurchase_rate, ROUND((COALESCE(cp.repurchase_rate, 0) - COALESCE(pp.repurchase_rate, 0))::numeric, 2)::FLOAT AS repurchase_growth FROM current_period cp, previous_period pp; END; $$; -- ------------------------------------------------------------------------------------- -- 2. 函数: rpc_analytics_user_growth_trend -- 描述: 获取用户增长与活跃趋势(日维度) -- ------------------------------------------------------------------------------------- CREATE OR REPLACE FUNCTION public.rpc_analytics_user_growth_trend( p_start_date DATE, p_end_date DATE ) RETURNS TABLE ( date DATE, new_users BIGINT, active_users 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, (SELECT COUNT(u.id) FROM public.ak_users u WHERE u.created_at::DATE = ds.date)::BIGINT AS new_users, (SELECT COUNT(DISTINCT bh.user_id) FROM public.ml_browse_history bh WHERE bh.created_at::DATE = ds.date)::BIGINT AS active_users FROM date_series ds ORDER BY ds.date; $$; -- ------------------------------------------------------------------------------------- -- 3. 函数: rpc_analytics_user_segments -- 描述: 获取用户分群(基于新老客、复购的简单模型) -- ------------------------------------------------------------------------------------- CREATE OR REPLACE FUNCTION public.rpc_analytics_user_segments( p_start_date DATE, p_end_date DATE ) RETURNS TABLE ( name TEXT, value BIGINT ) LANGUAGE sql AS $$ WITH user_orders_stats AS ( SELECT user_id, MIN(created_at) as first_order_time, COUNT(id) as total_orders FROM public.ml_orders WHERE payment_status = 2 GROUP BY user_id ), active_users_in_period AS ( SELECT DISTINCT user_id FROM public.ml_orders WHERE created_at::DATE BETWEEN p_start_date AND p_end_date ) SELECT segment as name, COUNT(user_id) as value FROM ( SELECT auip.user_id, CASE WHEN uos.first_order_time::DATE BETWEEN p_start_date AND p_end_date THEN '新客' WHEN uos.total_orders > 1 THEN '复购客户' ELSE '老客' END as segment FROM active_users_in_period auip JOIN user_orders_stats uos ON auip.user_id = uos.user_id ) segments GROUP BY segment; $$; -- ------------------------------------------------------------------------------------- -- 4. 函数: rpc_analytics_traffic_sources -- 描述: 获取流量来源分布 (基于注册来源) -- 兼容性说明:部分环境的 ak_users 可能不存在 registration_source 字段。 -- 为避免 RPC 报错导致页面加载失败,这里做“字段存在则分组统计,不存在则全部归为未知”的兼容。 -- ------------------------------------------------------------------------------------- CREATE OR REPLACE FUNCTION public.rpc_analytics_traffic_sources( p_start_date DATE, p_end_date DATE ) RETURNS TABLE ( name TEXT, value BIGINT ) LANGUAGE plpgsql AS $ DECLARE has_registration_source BOOLEAN := FALSE; BEGIN SELECT EXISTS ( SELECT 1 FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'ak_users' AND column_name = 'registration_source' ) INTO has_registration_source; IF has_registration_source THEN RETURN QUERY EXECUTE ' SELECT COALESCE(registration_source, ''未知'') AS name, COUNT(id)::BIGINT AS value FROM public.ak_users WHERE created_at::DATE BETWEEN $1 AND $2 GROUP BY name ORDER BY value DESC ' USING p_start_date, p_end_date; ELSE RETURN QUERY SELECT '未知'::TEXT AS name, COUNT(id)::BIGINT AS value FROM public.ak_users WHERE created_at::DATE BETWEEN p_start_date AND p_end_date; END IF; END; $; -- ===================================================================================== -- 5. 完成提示 -- ===================================================================================== DO $$ BEGIN RAISE NOTICE '======================================================='; RAISE NOTICE '商城分析模块 RPC 函数创建/更新完成!'; RAISE NOTICE '======================================================='; RAISE NOTICE '✓ RPC 函数创建/更新完成:'; RAISE NOTICE ' - rpc_analytics_user_kpis'; RAISE NOTICE ' - rpc_analytics_user_growth_trend'; RAISE NOTICE ' - rpc_analytics_user_segments'; RAISE NOTICE ' - rpc_analytics_traffic_sources'; RAISE NOTICE '======================================================='; RAISE NOTICE '下一步:'; RAISE NOTICE '1. 在数据库中执行此脚本。'; RAISE NOTICE '2. 确认前端 user-analysis.uvue 页面已调用这些 RPC。'; RAISE NOTICE '3. 如果缺少活跃数据,请确保 ml_browse_history 表有数据写入。'; RAISE NOTICE '======================================================='; END $$;