feat(admin): full integration of order, product, and finance modules with real RPC data streams

This commit is contained in:
comlibmb
2026-02-11 16:59:38 +08:00
parent cd7b92d496
commit 7e2246fec5
33 changed files with 2535 additions and 2093 deletions

View File

@@ -0,0 +1,78 @@
-- =====================================================================================
-- Admin 财务统计 - 余额收支分布统计 RPC
-- 位置docs/sql/30_rpc/finance/
-- 对象类型RPC 函数SECURITY DEFINER
-- 版本v1
-- 说明:按业务子类型统计指定时间范围内的余额收入与支出分布
-- =====================================================================================
CREATE OR REPLACE FUNCTION public.rpc_admin_balance_distribution(
p_start_time TIMESTAMP WITH TIME ZONE,
p_end_time TIMESTAMP WITH TIME ZONE
)
RETURNS JSONB
SECURITY DEFINER
SET search_path = public
LANGUAGE plpgsql
AS $$
DECLARE
v_total_income DECIMAL(12,2);
v_total_expense DECIMAL(12,2);
v_income_items JSONB;
v_expense_items JSONB;
BEGIN
-- 1. 权限检查
IF NOT EXISTS (
SELECT 1 FROM public.ak_users
WHERE id = auth.uid() AND role IN ('admin', 'analytics')
) THEN
RAISE EXCEPTION 'Permission denied';
END IF;
-- 2. 计算总收入与总支出
SELECT
COALESCE(SUM(number) FILTER (WHERE pm = 1), 0),
COALESCE(SUM(number) FILTER (WHERE pm = 0), 0)
INTO v_total_income, v_total_expense
FROM public.ml_user_bill
WHERE category = 'balance'
AND created_at >= p_start_time
AND created_at <= p_end_time
AND status = 1;
-- 3. 统计收入分布 (来源分析)
SELECT jsonb_agg(t) INTO v_income_items
FROM (
SELECT
type AS name,
SUM(number) AS value,
CASE WHEN v_total_income > 0 THEN ROUND(SUM(number) / v_total_income * 100, 2) ELSE 0 END AS percent
FROM public.ml_user_bill
WHERE category = 'balance' AND pm = 1 AND status = 1
AND created_at >= p_start_time AND created_at <= p_end_time
GROUP BY type
ORDER BY value DESC
) t;
-- 4. 统计支出分布 (消耗分析)
SELECT jsonb_agg(t) INTO v_expense_items
FROM (
SELECT
type AS name,
SUM(number) AS value,
CASE WHEN v_total_expense > 0 THEN ROUND(SUM(number) / v_total_expense * 100, 2) ELSE 0 END AS percent
FROM public.ml_user_bill
WHERE category = 'balance' AND pm = 0 AND status = 1
AND created_at >= p_start_time AND created_at <= p_end_time
GROUP BY type
ORDER BY value DESC
) t;
RETURN jsonb_build_object(
'income', COALESCE(v_income_items, '[]'::jsonb),
'expense', COALESCE(v_expense_items, '[]'::jsonb)
);
END;
$$;
COMMENT ON FUNCTION public.rpc_admin_balance_distribution IS '统计财务余额收支来源与消耗分布';

View File

@@ -0,0 +1,48 @@
-- =====================================================================================
-- Admin 财务统计 - 余额核心指标 RPC
-- 位置docs/sql/30_rpc/finance/
-- 对象类型RPC 函数SECURITY DEFINER
-- 版本v1
-- 说明:获取全站当前余额存量、累计增加总额及累计消耗总额
-- =====================================================================================
CREATE OR REPLACE FUNCTION public.rpc_admin_balance_stats()
RETURNS JSONB
SECURITY DEFINER
SET search_path = public
LANGUAGE plpgsql
AS $$
DECLARE
v_current_balance DECIMAL(12,2);
v_total_accumulation DECIMAL(12,2);
v_total_consumption DECIMAL(12,2);
BEGIN
-- 1. 权限检查
IF NOT EXISTS (
SELECT 1 FROM public.ak_users
WHERE id = auth.uid() AND role IN ('admin', 'analytics')
) THEN
RAISE EXCEPTION 'Permission denied';
END IF;
-- 2. 统计当前全站用户余额总存量
SELECT COALESCE(SUM(now_money), 0) INTO v_current_balance FROM public.ak_users;
-- 3. 统计累计增加 (pm=1) 和 累计消耗 (pm=0)
-- 基于 ml_user_bill 表中 category='balance' 的记录
SELECT
COALESCE(SUM(number) FILTER (WHERE pm = 1), 0),
COALESCE(SUM(number) FILTER (WHERE pm = 0), 0)
INTO v_total_accumulation, v_total_consumption
FROM public.ml_user_bill
WHERE category = 'balance' AND status = 1;
RETURN jsonb_build_object(
'current_balance', v_current_balance,
'total_accumulation', v_total_accumulation,
'total_consumption', v_total_consumption
);
END;
$$;
COMMENT ON FUNCTION public.rpc_admin_balance_stats IS '获取全站余额存量及累计收支汇总';

View File

@@ -0,0 +1,46 @@
-- =====================================================================================
-- Admin 财务统计 - 余额收支趋势 RPC
-- 位置docs/sql/30_rpc/finance/
-- 对象类型RPC 函数SECURITY DEFINER
-- 版本v1
-- 说明:按日聚合指定时间范围内的余额积累 (pm=1) 与 余额消耗 (pm=0) 趋势
-- =====================================================================================
CREATE OR REPLACE FUNCTION public.rpc_admin_balance_trend(
p_start_time TIMESTAMP WITH TIME ZONE,
p_end_time TIMESTAMP WITH TIME ZONE
)
RETURNS JSONB
SECURITY DEFINER
SET search_path = public
LANGUAGE plpgsql
AS $$
DECLARE
v_items JSONB;
BEGIN
-- 1. 权限检查
IF NOT EXISTS (
SELECT 1 FROM public.ak_users
WHERE id = auth.uid() AND role IN ('admin', 'analytics')
) THEN
RAISE EXCEPTION 'Permission denied';
END IF;
-- 2. 按日聚合统计
SELECT jsonb_agg(t) INTO v_items
FROM (
SELECT
to_char(date_trunc('day', gs.day), 'YYYY-MM-DD') AS date_group,
COALESCE(SUM(number) FILTER (WHERE pm = 1 AND category = 'balance'), 0) AS accumulation,
COALESCE(SUM(number) FILTER (WHERE pm = 0 AND category = 'balance'), 0) AS consumption
FROM generate_series(date_trunc('day', p_start_time), date_trunc('day', p_end_time), '1 day'::interval) gs(day)
LEFT JOIN public.ml_user_bill b ON date_trunc('day', b.created_at) = gs.day AND b.status = 1
GROUP BY gs.day
ORDER BY gs.day ASC
) t;
RETURN COALESCE(v_items, '[]'::jsonb);
END;
$$;
COMMENT ON FUNCTION public.rpc_admin_balance_trend IS '按日聚合财务余额收支趋势';

View File

@@ -0,0 +1,56 @@
-- =====================================================================================
-- Admin 财务功能 - 账单汇总统计 RPC
-- 位置docs/sql/30_rpc/finance/
-- 对象类型RPC 函数SECURITY DEFINER
-- 版本v1
-- 说明:按日/周/月维度聚合财务收支数据,支撑账单列表展示
-- =====================================================================================
CREATE OR REPLACE FUNCTION public.rpc_admin_finance_bill_summary(
p_start_time TIMESTAMP WITH TIME ZONE,
p_end_time TIMESTAMP WITH TIME ZONE,
p_interval TEXT DEFAULT 'day' -- day, week, month
)
RETURNS JSONB
SECURITY DEFINER
SET search_path = public
LANGUAGE plpgsql
AS $$
DECLARE
v_items JSONB;
BEGIN
-- 1. 权限检查
IF NOT EXISTS (
SELECT 1 FROM public.ak_users
WHERE id = auth.uid() AND role IN ('admin', 'analytics')
) THEN
RAISE EXCEPTION 'Permission denied';
END IF;
-- 2. 聚合统计
SELECT jsonb_agg(t) INTO v_items
FROM (
SELECT
to_char(date_trunc(p_interval, created_at),
CASE
WHEN p_interval = 'day' THEN 'YYYY-MM-DD'
WHEN p_interval = 'week' THEN 'IYYY-IW'
ELSE 'YYYY-MM'
END
) AS date_group,
SUM(number) FILTER (WHERE pm = 1) AS income,
SUM(number) FILTER (WHERE pm = 0) AS expense,
SUM(CASE WHEN pm = 1 THEN number ELSE -number END) AS net_entry
FROM public.ml_user_bill
WHERE created_at >= p_start_time
AND created_at <= p_end_time
AND status = 1
GROUP BY date_trunc(p_interval, created_at)
ORDER BY date_trunc(p_interval, created_at) DESC
) t;
RETURN COALESCE(v_items, '[]'::jsonb);
END;
$$;
COMMENT ON FUNCTION public.rpc_admin_finance_bill_summary IS '按周期聚合财务收支账单';

View File

@@ -0,0 +1,72 @@
-- =====================================================================================
-- Admin 财务功能 - 财务概况统计 RPC
-- 位置docs/sql/30_rpc/finance/
-- 对象类型RPC 函数SECURITY DEFINER
-- 版本v1
-- 说明:获取指定时间段内的财务核心 KPI营业额、充值汇总、提现汇总、资金存量
-- =====================================================================================
CREATE OR REPLACE FUNCTION public.rpc_admin_finance_overview(
p_start_time TIMESTAMP WITH TIME ZONE,
p_end_time TIMESTAMP WITH TIME ZONE
)
RETURNS JSONB
SECURITY DEFINER
SET search_path = public
LANGUAGE plpgsql
AS $$
DECLARE
v_recharge_amount DECIMAL(12,2);
v_recharge_count BIGINT;
v_extract_amount DECIMAL(12,2);
v_extract_count BIGINT;
v_total_user_balance DECIMAL(12,2);
v_total_user_brokerage DECIMAL(12,2);
BEGIN
-- 1. 权限检查
IF NOT EXISTS (
SELECT 1 FROM public.ak_users
WHERE id = auth.uid() AND role IN ('admin', 'analytics')
) THEN
RAISE EXCEPTION 'Permission denied';
END IF;
-- 2. 统计充值 (仅统计已支付)
SELECT
COALESCE(SUM(price + give_price), 0),
COUNT(*)
INTO v_recharge_amount, v_recharge_count
FROM public.ml_user_recharge
WHERE paid = 1
AND created_at >= p_start_time
AND created_at <= p_end_time;
-- 3. 统计提现 (仅统计已通过)
SELECT
COALESCE(SUM(extract_price), 0),
COUNT(*)
INTO v_extract_amount, v_extract_count
FROM public.ml_extract
WHERE status = 1
AND created_at >= p_start_time
AND created_at <= p_end_time;
-- 4. 统计全站资金存量 (实时快照)
SELECT
COALESCE(SUM(now_money), 0),
COALESCE(SUM(brokerage_price), 0)
INTO v_total_user_balance, v_total_user_brokerage
FROM public.ak_users;
RETURN jsonb_build_object(
'recharge_amount', v_recharge_amount,
'recharge_count', v_recharge_count,
'extract_amount', v_extract_amount,
'extract_count', v_extract_count,
'total_user_balance', v_total_user_balance,
'total_user_brokerage', v_total_user_brokerage
);
END;
$$;
COMMENT ON FUNCTION public.rpc_admin_finance_overview IS '财务核心 KPI 概况统计';

View File

@@ -0,0 +1,93 @@
-- =====================================================================================
-- RPC: rpc_admin_invoice_list
-- 位置docs/sql/30_rpc/finance/
-- 对象类型RPC 函数 (SECURITY DEFINER)
-- 版本v1
-- 说明:管理端分页获取发票申请列表,支持搜索、状态筛选及时间过滤
-- =====================================================================================
CREATE OR REPLACE FUNCTION public.rpc_admin_invoice_list(
p_page INTEGER DEFAULT 1,
p_page_size INTEGER DEFAULT 15,
p_status SMALLINT DEFAULT NULL,
p_start_time TIMESTAMP WITH TIME ZONE DEFAULT NULL,
p_end_time TIMESTAMP WITH TIME ZONE DEFAULT NULL,
p_search TEXT DEFAULT NULL
)
RETURNS JSONB
SECURITY DEFINER
SET search_path = public
LANGUAGE plpgsql
AS $$
DECLARE
v_offset INTEGER;
v_total BIGINT;
v_items JSONB;
BEGIN
-- 1. 权限检查
IF NOT EXISTS (
SELECT 1 FROM public.ak_users
WHERE id = auth.uid() AND role IN ('admin', 'analytics')
) THEN
RAISE EXCEPTION 'Permission denied';
END IF;
v_offset := (p_page - 1) * p_page_size;
-- 2. 获取总数
SELECT COUNT(*) INTO v_total
FROM public.ml_invoices i
LEFT JOIN public.ak_users u ON u.id = i.uid
WHERE (p_status IS NULL OR i.status = p_status)
AND (p_start_time IS NULL OR i.created_at >= p_start_time)
AND (p_end_time IS NULL OR i.created_at <= p_end_time)
AND (p_search IS NULL OR (
i.order_no ILIKE '%' || p_search || '%' OR
i.header_name ILIKE '%' || p_search || '%' OR
u.username ILIKE '%' || p_search || '%'
));
-- 3. 获取明细数据
SELECT jsonb_agg(t) INTO v_items
FROM (
SELECT
i.id,
i.uid,
i.order_no,
i.order_amount,
i.invoice_type,
i.header_type,
i.header_name,
i.tax_id,
i.email,
i.remark,
i.status,
i.refusal_reason,
i.invoice_url,
i.created_at,
i.updated_at,
u.username as user_name,
u.email as user_email
FROM public.ml_invoices i
LEFT JOIN public.ak_users u ON u.id = i.uid
WHERE (p_status IS NULL OR i.status = p_status)
AND (p_start_time IS NULL OR i.created_at >= p_start_time)
AND (p_end_time IS NULL OR i.created_at <= p_end_time)
AND (p_search IS NULL OR (
i.order_no ILIKE '%' || p_search || '%' OR
i.header_name ILIKE '%' || p_search || '%' OR
u.username ILIKE '%' || p_search || '%'
))
ORDER BY i.created_at DESC
LIMIT p_page_size
OFFSET v_offset
) t;
RETURN jsonb_build_object(
'total', v_total,
'items', COALESCE(v_items, '[]'::jsonb)
);
END;
$$;
COMMENT ON FUNCTION public.rpc_admin_invoice_list IS '管理员分页查询发票申请列表';

View File

@@ -0,0 +1,45 @@
-- =====================================================================================
-- RPC: rpc_admin_invoice_process
-- 位置docs/sql/30_rpc/finance/
-- 对象类型RPC 函数 (SECURITY DEFINER)
-- 版本v1
-- 说明:管理端处理发票申请(开票或驳回)
-- =====================================================================================
CREATE OR REPLACE FUNCTION public.rpc_admin_invoice_process(
p_id UUID,
p_status SMALLINT, -- 1: 已开票, -1: 已拒绝
p_invoice_url TEXT DEFAULT NULL,
p_refusal_reason TEXT DEFAULT NULL
)
RETURNS BOOLEAN
SECURITY DEFINER
SET search_path = public
LANGUAGE plpgsql
AS $$
DECLARE
v_ok BOOLEAN;
BEGIN
-- 1. 权限检查
IF NOT EXISTS (
SELECT 1 FROM public.ak_users
WHERE id = auth.uid() AND role IN ('admin', 'analytics')
) THEN
RAISE EXCEPTION 'Permission denied';
END IF;
-- 2. 更新状态
UPDATE public.ml_invoices
SET
status = p_status,
invoice_url = CASE WHEN p_status = 1 THEN p_invoice_url ELSE invoice_url END,
refusal_reason = CASE WHEN p_status = -1 THEN p_refusal_reason ELSE refusal_reason END,
updated_at = now()
WHERE id = p_id;
GET DIAGNOSTICS v_ok = ROW_COUNT;
RETURN v_ok;
END;
$$;
COMMENT ON FUNCTION public.rpc_admin_invoice_process IS '管理员处理发票开票申请';

View File

@@ -48,7 +48,7 @@ BEGIN
'refund_amount', refund_amount
) INTO v_stats FROM stats;
RETURN v_stats;
RETURN v_stats;
END;
$$;
@@ -86,17 +86,29 @@ BEGIN
p.main_image_url as image,
COALESCE(p.view_count, 0) as views,
(SELECT COUNT(DISTINCT user_id) FROM public.ml_browse_history bh WHERE bh.product_id = p.id AND bh.created_at BETWEEN p_start_time AND p_end_time) as visitors,
(SELECT COALESCE(SUM(quantity), 0) FROM public.ml_order_items oi JOIN public.ml_orders o ON oi.order_id = o.id
WHERE oi.product_id = p.id AND o.created_at BETWEEN p_start_time AND p_end_time AND o.order_status NOT IN (1, 5)) as sales,
(SELECT COALESCE(SUM(quantity), 0) FROM public.ml_shopping_cart sc WHERE sc.product_id = p.id AND sc.created_at BETWEEN p_start_time AND p_end_time) as cart_count,
(SELECT COUNT(DISTINCT o.id) FROM public.ml_orders o JOIN public.ml_order_items oi ON o.id = oi.order_id
WHERE oi.product_id = p.id AND o.created_at BETWEEN p_start_time AND p_end_time) as order_count,
(SELECT COALESCE(SUM(oi.quantity), 0) FROM public.ml_order_items oi JOIN public.ml_orders o ON oi.order_id = o.id
WHERE oi.product_id = p.id AND o.created_at BETWEEN p_start_time AND p_end_time AND o.order_status NOT IN (1, 5)) as pay_count,
(SELECT COALESCE(SUM(oi.total_amount), 0) FROM public.ml_order_items oi JOIN public.ml_orders o ON oi.order_id = o.id
WHERE oi.product_id = p.id AND o.created_at BETWEEN p_start_time AND p_end_time AND o.order_status NOT IN (1, 5)) as amount
WHERE oi.product_id = p.id AND o.created_at BETWEEN p_start_time AND p_end_time AND o.order_status NOT IN (1, 5)) as pay_amount,
(SELECT COUNT(*) FROM public.ml_user_favorites f WHERE f.target_id = p.id AND f.target_type = 1 AND f.created_at BETWEEN p_start_time AND p_end_time) as fav_count
FROM public.ml_products p
WHERE p.status != 4
ORDER BY
CASE WHEN p_sort_by = 'views' THEN 4
WHEN p_sort_by = 'sales' THEN 6
WHEN p_sort_by = 'amount' THEN 7
ELSE 6 END DESC
CASE
WHEN p_sort_by = 'views' THEN COALESCE(p.view_count, 0)
WHEN p_sort_by = 'sales' THEN (
SELECT COALESCE(SUM(oi.quantity), 0) FROM public.ml_order_items oi JOIN public.ml_orders o ON oi.order_id = o.id
WHERE oi.product_id = p.id AND o.created_at BETWEEN p_start_time AND p_end_time AND o.order_status NOT IN (1, 5)
)
WHEN p_sort_by = 'amount' THEN (
SELECT COALESCE(SUM(oi.total_amount), 0) FROM public.ml_order_items oi JOIN public.ml_orders o ON oi.order_id = o.id
WHERE oi.product_id = p.id AND o.created_at BETWEEN p_start_time AND p_end_time AND o.order_status NOT IN (1, 5)
)
ELSE COALESCE(p.view_count, 0)
END DESC
LIMIT p_limit
) t;

View File

@@ -0,0 +1,38 @@
-- =====================================================================================
-- Admin 商品管理 - 商品状态汇总统计 RPC
-- 位置docs/sql/30_rpc/product/
-- 对象类型RPC 函数SECURITY DEFINER
-- 版本v1
-- 说明:统计出售中、仓库中、草稿箱、回收站各状态的商品数量
-- =====================================================================================
CREATE OR REPLACE FUNCTION public.rpc_admin_product_count_stats()
RETURNS JSONB
SECURITY DEFINER
SET search_path = public
LANGUAGE plpgsql
AS $$
DECLARE
v_result JSONB;
BEGIN
-- 1. 权限检查
IF NOT EXISTS (
SELECT 1 FROM public.ak_users
WHERE auth_id = auth.uid() AND role IN ('admin', 'analytics')
) THEN
RAISE EXCEPTION 'Permission denied';
END IF;
-- 2. 统计各状态数量
-- status 定义1:上架(出售中), 2:下架(仓库中), 3:草稿, 4:逻辑删除(回收站)
SELECT jsonb_build_object(
'selling', COUNT(*) FILTER (WHERE status = 1),
'warehouse', COUNT(*) FILTER (WHERE status = 2),
'draft', COUNT(*) FILTER (WHERE status = 3),
'recycle', COUNT(*) FILTER (WHERE status = 4)
) INTO v_result
FROM public.ml_products;
RETURN v_result;
END;
$$;

View File

@@ -0,0 +1,44 @@
-- =====================================================================================
-- Admin 商品统计 - 营业趋势统计 RPC
-- 位置docs/sql/30_rpc/product/
-- 对象类型RPC 函数SECURITY DEFINER
-- 版本v1
-- 说明:按天聚合指定时间范围内的商品浏览量、访客量、支付金额及退款金额
-- =====================================================================================
CREATE OR REPLACE FUNCTION public.rpc_admin_product_trend(
p_start_time TIMESTAMP WITH TIME ZONE,
p_end_time TIMESTAMP WITH TIME ZONE
)
RETURNS JSONB
SECURITY DEFINER
SET search_path = public
LANGUAGE plpgsql
AS $$
DECLARE
v_items JSONB;
BEGIN
-- 1. 权限检查
IF NOT EXISTS (
SELECT 1 FROM public.ak_users
WHERE auth_id = auth.uid() AND role IN ('admin', 'analytics')
) THEN
RAISE EXCEPTION 'Permission denied';
END IF;
-- 2. 按日聚合统计
SELECT jsonb_agg(t) INTO v_items
FROM (
SELECT
to_char(date_trunc('day', gs.day), 'YYYY-MM-DD') AS date_group,
(SELECT COUNT(*) FROM public.ml_browse_history bh WHERE date_trunc('day', bh.created_at) = gs.day) as views,
(SELECT COUNT(DISTINCT user_id) FROM public.ml_browse_history bh WHERE date_trunc('day', bh.created_at) = gs.day) as visitors,
(SELECT COALESCE(SUM(total_amount), 0) FROM public.ml_orders o WHERE date_trunc('day', o.created_at) = gs.day AND o.order_status NOT IN (1, 5)) as pay_amount,
(SELECT COALESCE(SUM(total_amount), 0) FROM public.ml_orders o WHERE date_trunc('day', o.created_at) = gs.day AND o.order_status = 7) as refund_amount
FROM generate_series(date_trunc('day', p_start_time), date_trunc('day', p_end_time), '1 day'::interval) gs(day)
ORDER BY gs.day ASC
) t;
RETURN COALESCE(v_items, '[]'::jsonb);
END;
$$;