-- ===================================================================================== -- 商城分析模块增量升级脚本 -- 包含: 埋点事件表、分析用 RPC 函数 -- ===================================================================================== -- ===================================================================================== -- 1. 创建用户行为事件表 (埋点) -- ===================================================================================== CREATE TABLE IF NOT EXISTS public.analytics_events ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), event_type VARCHAR(50) NOT NULL, -- e.g., page_view, product_view, add_to_cart, checkout_start, payment_success user_id UUID REFERENCES public.ak_users(id) ON DELETE SET NULL, session_id VARCHAR(100), device_id VARCHAR(100), page_url TEXT, referrer TEXT, utm_source VARCHAR(100), utm_medium VARCHAR(100), utm_campaign VARCHAR(100), ip_address INET, user_agent TEXT, screen_resolution VARCHAR(20), device_type VARCHAR(50), os VARCHAR(50), browser VARCHAR(50), country VARCHAR(50), region VARCHAR(50), city VARCHAR(50), event_props JSONB DEFAULT '{}', created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); COMMENT ON TABLE public.analytics_events IS '用户行为事件(埋点)表'; COMMENT ON COLUMN public.analytics_events.event_type IS '事件类型,如 page_view, product_view, add_to_cart 等'; -- 创建索引 CREATE INDEX IF NOT EXISTS idx_analytics_events_user_id ON public.analytics_events(user_id); CREATE INDEX IF NOT EXISTS idx_analytics_events_event_type ON public.analytics_events(event_type); CREATE INDEX IF NOT EXISTS idx_analytics_events_created_at ON public.analytics_events(created_at); CREATE INDEX IF NOT EXISTS idx_analytics_events_session_id ON public.analytics_events(session_id); -- ===================================================================================== -- 2. 创建分析所需的 RPC 函数 -- ===================================================================================== -- ------------------------------------------------------------------------------------- -- 函数: 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, COUNT(DISTINCT e.user_id)::BIGINT AS active_users, COUNT(DISTINCT o.user_id)::BIGINT AS ordering_users, COUNT(DISTINCT CASE WHEN o.payment_status = 2 THEN o.user_id END)::BIGINT AS paid_users, ( SELECT COUNT(DISTINCT user_id) FROM public.ml_orders WHERE created_at::DATE BETWEEN p_start_date AND p_end_date AND user_id IN (SELECT id FROM public.ak_users WHERE created_at::DATE BETWEEN p_start_date AND p_end_date) ) * 100.0 / NULLIF(COUNT(DISTINCT CASE WHEN u.created_at::DATE BETWEEN p_start_date AND p_end_date THEN u.id END), 0) AS new_user_conversion_rate, 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) AS repurchase_rate FROM public.ak_users u LEFT JOIN public.analytics_events e ON e.user_id = u.id AND e.created_at::DATE BETWEEN p_start_date AND p_end_date LEFT JOIN public.ml_orders o ON o.user_id = u.id AND o.created_at::DATE BETWEEN p_start_date AND p_end_date LEFT JOIN ( SELECT user_id, COUNT(*) as order_count FROM public.ml_orders WHERE created_at::DATE <= p_end_date AND payment_status = 2 GROUP BY user_id ) user_orders ON user_orders.user_id = o.user_id AND o.payment_status = 2 ), -- 上一周期数据 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, COUNT(DISTINCT e.user_id)::BIGINT AS active_users, COUNT(DISTINCT o.user_id)::BIGINT AS ordering_users, COUNT(DISTINCT CASE WHEN o.payment_status = 2 THEN o.user_id END)::BIGINT AS paid_users, 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) AS repurchase_rate FROM public.ak_users u LEFT JOIN public.analytics_events e ON e.user_id = u.id AND e.created_at::DATE BETWEEN prev_start_date AND prev_end_date LEFT JOIN public.ml_orders o ON o.user_id = u.id AND o.created_at::DATE BETWEEN prev_start_date AND prev_end_date LEFT JOIN ( SELECT user_id, COUNT(*) as order_count FROM public.ml_orders WHERE created_at::DATE <= prev_end_date AND payment_status = 2 GROUP BY user_id ) user_orders ON user_orders.user_id = o.user_id AND o.payment_status = 2 ) SELECT cp.total_users, ROUND(((cp.total_users - pp.total_users) * 100.0 / NULLIF(pp.total_users, 1))::numeric, 2)::FLOAT AS user_growth, cp.new_users, ROUND(((cp.new_users - pp.new_users) * 100.0 / NULLIF(pp.new_users, 1))::numeric, 2)::FLOAT AS new_user_growth, cp.active_users, ROUND(((cp.active_users - pp.active_users) * 100.0 / NULLIF(pp.active_users, 1))::numeric, 2)::FLOAT AS active_growth, cp.ordering_users, ROUND(((cp.ordering_users - pp.ordering_users) * 100.0 / NULLIF(pp.ordering_users, 1))::numeric, 2)::FLOAT AS ordering_growth, cp.paid_users, ROUND(((cp.paid_users - pp.paid_users) * 100.0 / NULLIF(pp.paid_users, 1))::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((cp.repurchase_rate - pp.repurchase_rate)::numeric, 2)::FLOAT AS repurchase_growth FROM current_period cp, previous_period pp; END; $$; -- ------------------------------------------------------------------------------------- -- 函数: 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, COUNT(DISTINCT CASE WHEN u.created_at::DATE = ds.date THEN u.id END)::BIGINT AS new_users, COUNT(DISTINCT e.user_id)::BIGINT AS active_users FROM date_series ds LEFT JOIN public.ak_users u ON u.created_at::DATE = ds.date LEFT JOIN public.analytics_events e ON e.created_at::DATE = ds.date GROUP BY ds.date ORDER BY ds.date; $$; -- ------------------------------------------------------------------------------------- -- 函数: 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 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 ) SELECT segment as name, COUNT(user_id) as value FROM ( SELECT u.id as user_id, CASE WHEN uo.first_order_time::DATE BETWEEN p_start_date AND p_end_date THEN '新客' WHEN uo.total_orders > 1 AND EXISTS (SELECT 1 FROM public.ml_orders WHERE user_id = u.id AND created_at::DATE BETWEEN p_start_date AND p_end_date) THEN '复购客户' WHEN uo.total_orders >= 1 AND EXISTS (SELECT 1 FROM public.ml_orders WHERE user_id = u.id AND created_at::DATE BETWEEN p_start_date AND p_end_date) THEN '回流客户' ELSE '老客' END as segment FROM public.ak_users u JOIN user_orders uo ON u.id = uo.user_id ) segments GROUP BY segment; $$; -- ------------------------------------------------------------------------------------- -- 函数: rpc_analytics_traffic_sources -- 描述: 获取流量来源分布 -- ------------------------------------------------------------------------------------- CREATE OR REPLACE FUNCTION public.rpc_analytics_traffic_sources( p_start_date DATE, p_end_date DATE ) RETURNS TABLE ( name TEXT, value BIGINT ) LANGUAGE sql AS $$ SELECT COALESCE( NULLIF(utm_source, ''), CASE WHEN referrer IS NULL OR referrer = '' THEN '直接访问' WHEN referrer ILIKE '%baidu.com%' THEN '百度搜索' WHEN referrer ILIKE '%google.com%' THEN '谷歌搜索' WHEN referrer ILIKE '%bing.com%' THEN '必应搜索' WHEN referrer ILIKE '%sogou.com%' THEN '搜狗搜索' WHEN referrer ILIKE '%toutiao.com%' OR referrer ILIKE '%douyin.com%' THEN '字节系' WHEN referrer ILIKE '%weixin.qq.com%' THEN '微信' ELSE '其他推荐' END ) AS name, COUNT(DISTINCT session_id)::BIGINT AS value FROM public.analytics_events WHERE created_at::DATE BETWEEN p_start_date AND p_end_date AND event_type = 'page_view' GROUP BY name ORDER BY value DESC; $$; -- ===================================================================================== -- 3. 完成提示 -- ===================================================================================== DO $$ BEGIN RAISE NOTICE '======================================================='; RAISE NOTICE '商城分析模块增量升级完成!'; RAISE NOTICE '======================================================='; RAISE NOTICE '✓ 埋点事件表(analytics_events)创建完成'; 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. 在应用中集成前端埋点,将数据写入 analytics_events 表'; RAISE NOTICE '2. 在前端分析页面调用以上 RPC 函数获取数据'; RAISE NOTICE '======================================================='; END $$;