247 lines
11 KiB
PL/PgSQL
247 lines
11 KiB
PL/PgSQL
-- =====================================================================================
|
||
-- 商城分析模块 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 $$;
|