feat(admin): full integration of order, product, and finance modules with real RPC data streams
This commit is contained in:
39
docs/sql/10_schema/finance/ml_extract_v1.sql
Normal file
39
docs/sql/10_schema/finance/ml_extract_v1.sql
Normal file
@@ -0,0 +1,39 @@
|
||||
-- =====================================================================================
|
||||
-- Schema: 用户提现申请表
|
||||
-- 位置:docs/sql/10_schema/finance/
|
||||
-- 对象类型:Schema (DDL)
|
||||
-- 版本:v1
|
||||
-- 说明:管理用户发起的提现申请(佣金/余额),支持多种提现方式及快照信息
|
||||
-- =====================================================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.ml_extract (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
uid UUID NOT NULL REFERENCES public.ak_users(id),
|
||||
|
||||
real_name TEXT NULL, -- 提现人姓名快照
|
||||
extract_type TEXT NOT NULL, -- 提现方式: alipay, wechat, bank
|
||||
|
||||
-- 账号快照信息
|
||||
alipay_code TEXT NULL, -- 支付宝账号
|
||||
wechat_code TEXT NULL, -- 微信账号
|
||||
bank_code TEXT NULL, -- 银行卡号
|
||||
bank_address TEXT NULL, -- 开户行地址
|
||||
|
||||
extract_price DECIMAL(12,2) NOT NULL DEFAULT 0, -- 申请提现金额
|
||||
service_fee DECIMAL(12,2) NOT NULL DEFAULT 0, -- 提现手续费
|
||||
balance DECIMAL(12,2) NOT NULL DEFAULT 0, -- 提现时的余额快照
|
||||
|
||||
status SMALLINT NOT NULL DEFAULT 0, -- 状态: 0:待审核, 1:已通过, -1:已驳回
|
||||
refusal_reason TEXT NULL, -- 驳回原因
|
||||
|
||||
admin_id UUID NULL REFERENCES public.ak_users(id), -- 审核人ID
|
||||
payment_time TIMESTAMPTZ NULL, -- 打款/到账时间
|
||||
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
-- 索引
|
||||
CREATE INDEX IF NOT EXISTS ml_extract_uid_idx ON public.ml_extract (uid);
|
||||
CREATE INDEX IF NOT EXISTS ml_extract_status_idx ON public.ml_extract (status);
|
||||
CREATE INDEX IF NOT EXISTS ml_extract_created_at_idx ON public.ml_extract (created_at DESC);
|
||||
36
docs/sql/10_schema/finance/ml_invoices_v1.sql
Normal file
36
docs/sql/10_schema/finance/ml_invoices_v1.sql
Normal file
@@ -0,0 +1,36 @@
|
||||
-- =====================================================================================
|
||||
-- Schema: 发票管理表
|
||||
-- 位置:docs/sql/10_schema/finance/ml_invoices_v1.sql
|
||||
-- 对象类型:Schema (DDL)
|
||||
-- 版本:v1
|
||||
-- 说明:记录用户提交的开票申请及其处理状态
|
||||
-- =====================================================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.ml_invoices (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
uid UUID NOT NULL REFERENCES public.ak_users(id),
|
||||
order_no TEXT NOT NULL, -- 关联订单号
|
||||
|
||||
order_amount DECIMAL(12,2) NOT NULL, -- 订单金额
|
||||
|
||||
invoice_type SMALLINT NOT NULL DEFAULT 1, -- 1: 电子普通发票, 2: 增值税专用发票
|
||||
header_type SMALLINT NOT NULL DEFAULT 1, -- 1: 个人, 2: 企业
|
||||
|
||||
header_name TEXT NOT NULL, -- 发票抬头
|
||||
tax_id TEXT NULL, -- 企业税号
|
||||
|
||||
email TEXT NULL, -- 接收邮箱
|
||||
remark TEXT NULL, -- 备注
|
||||
|
||||
status SMALLINT NOT NULL DEFAULT 0, -- 0: 待开票, 1: 已开票, -1: 已拒绝
|
||||
refusal_reason TEXT NULL, -- 驳回原因
|
||||
invoice_url TEXT NULL, -- 电子发票文件路径/URL
|
||||
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
-- 索引
|
||||
CREATE INDEX IF NOT EXISTS ml_invoices_uid_idx ON public.ml_invoices (uid);
|
||||
CREATE INDEX IF NOT EXISTS ml_invoices_order_no_idx ON public.ml_invoices (order_no);
|
||||
CREATE INDEX IF NOT EXISTS ml_invoices_status_idx ON public.ml_invoices (status);
|
||||
33
docs/sql/10_schema/finance/ml_user_bill_v1.sql
Normal file
33
docs/sql/10_schema/finance/ml_user_bill_v1.sql
Normal file
@@ -0,0 +1,33 @@
|
||||
-- =====================================================================================
|
||||
-- Schema: 用户资金流水表
|
||||
-- 位置:docs/sql/10_schema/finance/
|
||||
-- 对象类型:Schema (DDL)
|
||||
-- 版本:v1
|
||||
-- 说明:记录用户余额、积分、佣金的所有增减流水(原子日志)
|
||||
-- =====================================================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.ml_user_bill (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
uid UUID NOT NULL REFERENCES public.ak_users(id),
|
||||
|
||||
link_id TEXT NULL, -- 关联业务ID(订单号、提现ID、充值ID等)
|
||||
pm SMALLINT NOT NULL DEFAULT 1, -- 0:支出, 1:收入
|
||||
|
||||
title TEXT NOT NULL, -- 流水标题(如:商品购买、充值、提现)
|
||||
category TEXT NOT NULL, -- 业务大类(如:balance-余额, integral-积分, brokerage-佣金)
|
||||
type TEXT NOT NULL, -- 业务子类型(如:recharge, extract, pay, refund, system_add, system_sub)
|
||||
|
||||
number DECIMAL(12,2) NOT NULL DEFAULT 0, -- 变动金额
|
||||
balance DECIMAL(12,2) NOT NULL DEFAULT 0, -- 变动后的余额快照
|
||||
|
||||
mark TEXT NULL, -- 备注
|
||||
status SMALLINT NOT NULL DEFAULT 1, -- 状态(1:有效, 0:无效/冲正)
|
||||
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
-- 常用查询索引
|
||||
CREATE INDEX IF NOT EXISTS ml_user_bill_uid_idx ON public.ml_user_bill (uid);
|
||||
CREATE INDEX IF NOT EXISTS ml_user_bill_category_type_idx ON public.ml_user_bill (category, type);
|
||||
CREATE INDEX IF NOT EXISTS ml_user_bill_created_at_idx ON public.ml_user_bill (created_at DESC);
|
||||
32
docs/sql/10_schema/finance/ml_user_recharge_v1.sql
Normal file
32
docs/sql/10_schema/finance/ml_user_recharge_v1.sql
Normal file
@@ -0,0 +1,32 @@
|
||||
-- =====================================================================================
|
||||
-- Schema: 用户充值记录表
|
||||
-- 位置:docs/sql/10_schema/finance/
|
||||
-- 对象类型:Schema (DDL)
|
||||
-- 版本:v1
|
||||
-- 说明:记录用户主动发起的充值申请及支付状态
|
||||
-- =====================================================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.ml_user_recharge (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
uid UUID NOT NULL REFERENCES public.ak_users(id),
|
||||
|
||||
order_no TEXT UNIQUE NOT NULL, -- 充值订单号(cz开头)
|
||||
recharge_type TEXT NOT NULL, -- 充值渠道: wechat, alipay, system (后台补单)
|
||||
|
||||
price DECIMAL(12,2) NOT NULL DEFAULT 0, -- 实际充值金额
|
||||
give_price DECIMAL(12,2) NOT NULL DEFAULT 0, -- 赠送金额
|
||||
|
||||
paid SMALLINT NOT NULL DEFAULT 0, -- 支付状态: 0:未支付, 1:已支付
|
||||
pay_time TIMESTAMPTZ NULL, -- 支付时间
|
||||
|
||||
channel_trade_no TEXT NULL, -- 外部渠道流水号
|
||||
status SMALLINT NOT NULL DEFAULT 1, -- 记录状态: 1:正常, 0:逻辑删除
|
||||
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
-- 索引
|
||||
CREATE INDEX IF NOT EXISTS ml_user_recharge_uid_idx ON public.ml_user_recharge (uid);
|
||||
CREATE INDEX IF NOT EXISTS ml_user_recharge_order_no_idx ON public.ml_user_recharge (order_no);
|
||||
CREATE INDEX IF NOT EXISTS ml_user_recharge_created_at_idx ON public.ml_user_recharge (created_at DESC);
|
||||
20
docs/sql/20_rls/finance/ml_extract_rls_v1.sql
Normal file
20
docs/sql/20_rls/finance/ml_extract_rls_v1.sql
Normal file
@@ -0,0 +1,20 @@
|
||||
-- =====================================================================================
|
||||
-- RLS: 用户提现申请表
|
||||
-- 位置:docs/sql/20_rls/finance/
|
||||
-- 对象类型:RLS 策略
|
||||
-- 版本:v1
|
||||
-- 说明:仅允许用户查看自己的提现记录;管理端通过 RPC 访问
|
||||
-- =====================================================================================
|
||||
|
||||
ALTER TABLE public.ml_extract ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
-- 策略 1: 允许用户读取自己的提现申请
|
||||
DROP POLICY IF EXISTS ml_extract_user_select ON public.ml_extract;
|
||||
CREATE POLICY ml_extract_user_select
|
||||
ON public.ml_extract
|
||||
FOR SELECT
|
||||
TO authenticated
|
||||
USING (uid = auth.uid());
|
||||
|
||||
-- 默认不开放 INSERT/UPDATE/DELETE 给普通用户
|
||||
-- 提现申请通常由特定的 RPC 函数 (security definer) 创建,以确保业务逻辑(如冻结余额)的原子性
|
||||
19
docs/sql/20_rls/finance/ml_invoices_rls_v1.sql
Normal file
19
docs/sql/20_rls/finance/ml_invoices_rls_v1.sql
Normal file
@@ -0,0 +1,19 @@
|
||||
-- =====================================================================================
|
||||
-- RLS: 发票管理表
|
||||
-- 位置:docs/sql/20_rls/finance/ml_invoices_rls_v1.sql
|
||||
-- 对象类型:RLS 策略
|
||||
-- 版本:v1
|
||||
-- 说明:用户仅能查看自己的开票申请;管理端通过 RPC 访问
|
||||
-- =====================================================================================
|
||||
|
||||
ALTER TABLE public.ml_invoices ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
-- 策略 1: 允许用户读取自己的记录
|
||||
DROP POLICY IF EXISTS ml_invoices_user_select ON public.ml_invoices;
|
||||
CREATE POLICY ml_invoices_user_select
|
||||
ON public.ml_invoices
|
||||
FOR SELECT
|
||||
TO authenticated
|
||||
USING (uid = auth.uid());
|
||||
|
||||
-- 默认不开放 INSERT/UPDATE/DELETE 给普通用户,通常由 RPC 或支付后逻辑触发
|
||||
19
docs/sql/20_rls/finance/ml_user_bill_rls_v1.sql
Normal file
19
docs/sql/20_rls/finance/ml_user_bill_rls_v1.sql
Normal file
@@ -0,0 +1,19 @@
|
||||
-- =====================================================================================
|
||||
-- RLS: 用户资金流水表
|
||||
-- 位置:docs/sql/20_rls/finance/
|
||||
-- 对象类型:RLS 策略
|
||||
-- 版本:v1
|
||||
-- 说明:仅允许用户查看自己的流水记录;管理端通过 RPC 访问
|
||||
-- =====================================================================================
|
||||
|
||||
ALTER TABLE public.ml_user_bill ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
-- 策略 1: 允许用户读取自己的记录
|
||||
DROP POLICY IF EXISTS ml_user_bill_user_select ON public.ml_user_bill;
|
||||
CREATE POLICY ml_user_bill_user_select
|
||||
ON public.ml_user_bill
|
||||
FOR SELECT
|
||||
TO authenticated
|
||||
USING (uid = auth.uid());
|
||||
|
||||
-- 默认不开放 INSERT/UPDATE/DELETE 给普通用户,由后端逻辑或 RPC 触发
|
||||
19
docs/sql/20_rls/finance/ml_user_recharge_rls_v1.sql
Normal file
19
docs/sql/20_rls/finance/ml_user_recharge_rls_v1.sql
Normal file
@@ -0,0 +1,19 @@
|
||||
-- =====================================================================================
|
||||
-- RLS: 用户充值记录表
|
||||
-- 位置:docs/sql/20_rls/finance/
|
||||
-- 对象类型:RLS 策略
|
||||
-- 版本:v1
|
||||
-- 说明:仅允许用户查看自己的充值记录;管理端通过 RPC 访问
|
||||
-- =====================================================================================
|
||||
|
||||
ALTER TABLE public.ml_user_recharge ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
-- 策略 1: 允许用户读取自己的记录
|
||||
DROP POLICY IF EXISTS ml_user_recharge_user_select ON public.ml_user_recharge;
|
||||
CREATE POLICY ml_user_recharge_user_select
|
||||
ON public.ml_user_recharge
|
||||
FOR SELECT
|
||||
TO authenticated
|
||||
USING (uid = auth.uid());
|
||||
|
||||
-- 默认不开放 INSERT/UPDATE/DELETE 给普通用户,写操作通常由业务逻辑或支付回调触发
|
||||
@@ -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 '统计财务余额收支来源与消耗分布';
|
||||
48
docs/sql/30_rpc/finance/rpc_admin_balance_stats_v1.sql
Normal file
48
docs/sql/30_rpc/finance/rpc_admin_balance_stats_v1.sql
Normal 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 '获取全站余额存量及累计收支汇总';
|
||||
46
docs/sql/30_rpc/finance/rpc_admin_balance_trend_v1.sql
Normal file
46
docs/sql/30_rpc/finance/rpc_admin_balance_trend_v1.sql
Normal 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 '按日聚合财务余额收支趋势';
|
||||
@@ -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 '按周期聚合财务收支账单';
|
||||
72
docs/sql/30_rpc/finance/rpc_admin_finance_overview_v1.sql
Normal file
72
docs/sql/30_rpc/finance/rpc_admin_finance_overview_v1.sql
Normal 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 概况统计';
|
||||
93
docs/sql/30_rpc/finance/rpc_admin_invoice_list_v1.sql
Normal file
93
docs/sql/30_rpc/finance/rpc_admin_invoice_list_v1.sql
Normal 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 '管理员分页查询发票申请列表';
|
||||
45
docs/sql/30_rpc/finance/rpc_admin_invoice_process_v1.sql
Normal file
45
docs/sql/30_rpc/finance/rpc_admin_invoice_process_v1.sql
Normal 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 '管理员处理发票开票申请';
|
||||
@@ -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;
|
||||
|
||||
|
||||
38
docs/sql/30_rpc/product/rpc_admin_product_count_stats_v1.sql
Normal file
38
docs/sql/30_rpc/product/rpc_admin_product_count_stats_v1.sql
Normal 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;
|
||||
$$;
|
||||
44
docs/sql/30_rpc/product/rpc_admin_product_trend_v1.sql
Normal file
44
docs/sql/30_rpc/product/rpc_admin_product_trend_v1.sql
Normal 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;
|
||||
$$;
|
||||
Reference in New Issue
Block a user