diff --git a/docs/sql/10_schema/user/ak_users_finance_fields_v1.sql b/docs/sql/10_schema/user/ak_users_finance_fields_v1.sql
new file mode 100644
index 00000000..ebe40bd2
--- /dev/null
+++ b/docs/sql/10_schema/user/ak_users_finance_fields_v1.sql
@@ -0,0 +1,13 @@
+-- =====================================================================================
+-- User 模块扩展 - 财务字段补全
+-- 位置:docs/sql/10_schema/user/
+-- 版本:v1
+-- 描述:为 ak_users 增加余额与佣金字段,支持财务业务。
+-- =====================================================================================
+
+ALTER TABLE public.ak_users
+ADD COLUMN IF NOT EXISTS now_money DECIMAL(12,2) DEFAULT 0,
+ADD COLUMN IF NOT EXISTS brokerage_price DECIMAL(12,2) DEFAULT 0;
+
+COMMENT ON COLUMN public.ak_users.now_money IS '用户当前余额';
+COMMENT ON COLUMN public.ak_users.brokerage_price IS '用户当前佣金';
diff --git a/docs/sql/30_rpc/admin/rpc_admin_system_config_get_v1.sql b/docs/sql/30_rpc/admin/rpc_admin_system_config_get_v1.sql
new file mode 100644
index 00000000..da23eb2a
--- /dev/null
+++ b/docs/sql/30_rpc/admin/rpc_admin_system_config_get_v1.sql
@@ -0,0 +1,35 @@
+-- =====================================================================================
+-- Admin 系统功能 - 获取配置项 RPC
+-- 位置:docs/sql/30_rpc/admin/
+-- 对象类型:RPC 函数(SECURITY DEFINER)
+-- 版本:v1
+-- 依赖:ml_system_configs, ak_users 表已存在
+-- =====================================================================================
+
+CREATE OR REPLACE FUNCTION public.rpc_admin_system_config_get(
+ p_key TEXT
+)
+RETURNS JSONB
+SECURITY DEFINER
+SET search_path = public
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ v_value 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 config_value INTO v_value
+ FROM public.ml_system_configs
+ WHERE config_key = p_key;
+
+ RETURN v_value;
+END;
+$$;
\ No newline at end of file
diff --git a/docs/sql/30_rpc/admin/rpc_admin_system_config_save_v1.sql b/docs/sql/30_rpc/admin/rpc_admin_system_config_save_v1.sql
new file mode 100644
index 00000000..bf82d551
--- /dev/null
+++ b/docs/sql/30_rpc/admin/rpc_admin_system_config_save_v1.sql
@@ -0,0 +1,37 @@
+-- =====================================================================================
+-- Admin 系统功能 - 保存配置项 RPC
+-- 位置:docs/sql/30_rpc/admin/
+-- 对象类型:RPC 函数(SECURITY DEFINER)
+-- 版本:v1
+-- 依赖:ml_system_configs, ak_users 表已存在
+-- =====================================================================================
+
+CREATE OR REPLACE FUNCTION public.rpc_admin_system_config_save(
+ p_key TEXT,
+ p_value JSONB
+)
+RETURNS BOOLEAN
+SECURITY DEFINER
+SET search_path = public
+LANGUAGE plpgsql
+AS $$
+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. 执行保存(存在则更新,不存在则插入)
+ INSERT INTO public.ml_system_configs (config_key, config_value, updated_at)
+ VALUES (p_key, p_value, NOW())
+ ON CONFLICT (config_key)
+ DO UPDATE SET
+ config_value = EXCLUDED.config_value,
+ updated_at = NOW();
+
+ RETURN TRUE;
+END;
+$$;
\ No newline at end of file
diff --git a/docs/sql/30_rpc/finance/rpc_admin_extract_list_v1.sql b/docs/sql/30_rpc/finance/rpc_admin_extract_list_v1.sql
new file mode 100644
index 00000000..9b9b239f
--- /dev/null
+++ b/docs/sql/30_rpc/finance/rpc_admin_extract_list_v1.sql
@@ -0,0 +1,103 @@
+-- =====================================================================================
+-- Admin 财务功能 - 提现申请列表分页查询 RPC
+-- 位置:docs/sql/30_rpc/finance/
+-- 对象类型:RPC 函数(SECURITY DEFINER)
+-- 版本:v1
+-- 依赖:ml_extract, ak_users 表已存在
+-- 权限:仅 admin 角色可执行(口径 A:全局数据访问通过 RPC)
+-- =====================================================================================
+
+CREATE OR REPLACE FUNCTION public.rpc_admin_extract_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 = 'admin'
+ ) THEN
+ RAISE EXCEPTION 'Permission denied';
+ END IF;
+
+ v_offset := (p_page - 1) * p_page_size;
+
+ -- 2. 获取总数
+ SELECT COUNT(*) INTO v_total
+ FROM public.ml_extract e
+ LEFT JOIN public.ak_users u ON u.id = e.uid
+ WHERE (p_status IS NULL OR e.status = p_status)
+ AND (p_start_time IS NULL OR e.created_at >= p_start_time)
+ AND (p_end_time IS NULL OR e.created_at <= p_end_time)
+ AND (p_search IS NULL OR (
+ COALESCE(u.username, '') ILIKE '%' || p_search || '%' OR
+ COALESCE(u.email, '') ILIKE '%' || p_search || '%' OR
+ COALESCE(e.real_name, '') ILIKE '%' || p_search || '%' OR
+ COALESCE(e.bank_code, '') ILIKE '%' || p_search || '%' OR
+ COALESCE(e.alipay_code, '') ILIKE '%' || p_search || '%' OR
+ COALESCE(e.wechat_code, '') ILIKE '%' || p_search || '%'
+ ));
+
+ -- 3. 获取明细数据
+ SELECT jsonb_agg(t) INTO v_items
+ FROM (
+ SELECT
+ e.id,
+ e.uid,
+ e.real_name,
+ e.extract_type,
+ e.bank_code,
+ e.bank_address,
+ e.alipay_code,
+ e.wechat_code,
+ e.extract_price,
+ e.service_fee,
+ e.balance,
+ e.status,
+ e.refusal_reason,
+ e.admin_id,
+ e.payment_time,
+ e.created_at,
+ e.updated_at,
+ u.username as user_name,
+ u.email as user_email
+ FROM public.ml_extract e
+ LEFT JOIN public.ak_users u ON u.id = e.uid
+ WHERE (p_status IS NULL OR e.status = p_status)
+ AND (p_start_time IS NULL OR e.created_at >= p_start_time)
+ AND (p_end_time IS NULL OR e.created_at <= p_end_time)
+ AND (p_search IS NULL OR (
+ COALESCE(u.username, '') ILIKE '%' || p_search || '%' OR
+ COALESCE(u.email, '') ILIKE '%' || p_search || '%' OR
+ COALESCE(e.real_name, '') ILIKE '%' || p_search || '%' OR
+ COALESCE(e.bank_code, '') ILIKE '%' || p_search || '%' OR
+ COALESCE(e.alipay_code, '') ILIKE '%' || p_search || '%' OR
+ COALESCE(e.wechat_code, '') ILIKE '%' || p_search || '%'
+ ))
+ ORDER BY e.created_at DESC
+ LIMIT p_page_size
+ OFFSET v_offset
+ ) t;
+
+ -- 4. 返回结果
+ RETURN jsonb_build_object(
+ 'total', v_total,
+ 'items', COALESCE(v_items, '[]'::jsonb)
+ );
+END;
+$$;
+
+COMMENT ON FUNCTION public.rpc_admin_extract_list IS '管理员提现申请列表分页查询';
diff --git a/docs/sql/30_rpc/finance/rpc_admin_extract_review_v1.sql b/docs/sql/30_rpc/finance/rpc_admin_extract_review_v1.sql
new file mode 100644
index 00000000..35272b68
--- /dev/null
+++ b/docs/sql/30_rpc/finance/rpc_admin_extract_review_v1.sql
@@ -0,0 +1,78 @@
+-- =====================================================================================
+-- Admin 财务功能 - 提现审核 RPC (口径 2)
+-- 位置:docs/sql/30_rpc/finance/
+-- 版本:v1
+-- 描述:提现审核通过时才扣除佣金并生成流水。
+-- 安全策略:SECURITY DEFINER, 入口鉴权, 固定 search_path
+-- =====================================================================================
+
+CREATE OR REPLACE FUNCTION public.rpc_admin_extract_review(
+ p_extract_id UUID,
+ p_status SMALLINT, -- 1: 通过, -1: 驳回
+ p_refusal_reason TEXT DEFAULT NULL
+)
+RETURNS VOID
+SECURITY DEFINER
+SET search_path = public
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ v_extract RECORD;
+ v_user RECORD;
+BEGIN
+ -- 1. 鉴权:仅 admin 角色可执行
+ IF NOT EXISTS (
+ SELECT 1 FROM public.ak_users
+ WHERE id = auth.uid() AND role = 'admin'
+ ) THEN
+ RAISE EXCEPTION 'Permission denied';
+ END IF;
+
+ -- 2. 锁定并获取提现记录
+ SELECT * INTO v_extract FROM public.ml_extract WHERE id = p_extract_id FOR UPDATE;
+ IF NOT FOUND THEN RAISE EXCEPTION 'Extract record not found'; END IF;
+ IF v_extract.status != 0 THEN RAISE EXCEPTION 'Record already processed'; END IF;
+
+ -- 3. 业务处理
+ IF p_status = 1 THEN
+ -- 审核通过:锁定并校验用户资金
+ SELECT * INTO v_user FROM public.ak_users WHERE id = v_extract.uid FOR UPDATE;
+ IF v_user.brokerage_price < v_extract.extract_price THEN
+ RAISE EXCEPTION 'Insufficient brokerage balance';
+ END IF;
+
+ -- 扣除佣金
+ UPDATE public.ak_users
+ SET brokerage_price = brokerage_price - v_extract.extract_price
+ WHERE id = v_extract.uid;
+
+ -- 写入资金流水
+ INSERT INTO public.ml_user_bill (uid, link_id, pm, title, category, type, number, balance)
+ VALUES (
+ v_extract.uid,
+ p_extract_id::TEXT,
+ 0, -- 支出
+ '佣金提现',
+ 'brokerage',
+ 'extract',
+ v_extract.extract_price,
+ v_user.brokerage_price - v_extract.extract_price
+ );
+
+ -- 更新提现记录
+ UPDATE public.ml_extract
+ SET status = 1, admin_id = auth.uid(), payment_time = now()
+ WHERE id = p_extract_id;
+
+ ELSIF p_status = -1 THEN
+ -- 审核驳回:仅更新状态
+ UPDATE public.ml_extract
+ SET status = -1, refusal_reason = p_refusal_reason, admin_id = auth.uid()
+ WHERE id = p_extract_id;
+ ELSE
+ RAISE EXCEPTION 'Invalid status';
+ END IF;
+END;
+$$;
+
+COMMENT ON FUNCTION public.rpc_admin_extract_review IS '管理员审核提现申请(口径 2:通过时扣款)';
diff --git a/docs/sql/30_rpc/finance/rpc_admin_recharge_audit_v1.sql b/docs/sql/30_rpc/finance/rpc_admin_recharge_audit_v1.sql
new file mode 100644
index 00000000..fc6d8008
--- /dev/null
+++ b/docs/sql/30_rpc/finance/rpc_admin_recharge_audit_v1.sql
@@ -0,0 +1,64 @@
+-- =====================================================================================
+-- Admin 财务功能 - 充值补单/审计 RPC
+-- 位置:docs/sql/30_rpc/finance/
+-- 版本:v1
+-- 描述:由管理员发起的人工充值补单或离线支付审计确认。
+-- 安全策略:SECURITY DEFINER, 入口鉴权, 固定 search_path
+-- =====================================================================================
+
+CREATE OR REPLACE FUNCTION public.rpc_admin_recharge_audit(
+ p_recharge_id UUID,
+ p_mark TEXT DEFAULT '管理员人工审计/补单'
+)
+RETURNS VOID
+SECURITY DEFINER
+SET search_path = public
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ v_recharge RECORD;
+ v_user RECORD;
+BEGIN
+ -- 1. 鉴权:仅 admin 角色可执行
+ IF NOT EXISTS (
+ SELECT 1 FROM public.ak_users
+ WHERE id = auth.uid() AND role = 'admin'
+ ) THEN
+ RAISE EXCEPTION 'Permission denied';
+ END IF;
+
+ -- 2. 锁定并获取充值记录
+ SELECT * INTO v_recharge FROM public.ml_user_recharge WHERE id = p_recharge_id FOR UPDATE;
+ IF NOT FOUND THEN RAISE EXCEPTION 'Recharge record not found'; END IF;
+ IF v_recharge.paid = 1 THEN RAISE EXCEPTION 'Recharge already paid'; END IF;
+
+ -- 3. 锁定并更新用户余额
+ SELECT * INTO v_user FROM public.ak_users WHERE id = v_recharge.uid FOR UPDATE;
+
+ UPDATE public.ak_users
+ SET now_money = now_money + v_recharge.price + v_recharge.give_price
+ WHERE id = v_recharge.uid;
+
+ -- 4. 写入资金流水
+ INSERT INTO public.ml_user_bill (uid, link_id, pm, title, category, type, number, balance, mark)
+ VALUES (
+ v_recharge.uid,
+ v_recharge.order_no,
+ 1, -- 收入
+ '用户充值',
+ 'now_money',
+ 'recharge',
+ v_recharge.price + v_recharge.give_price,
+ v_user.now_money + v_recharge.price + v_recharge.give_price,
+ p_mark
+ );
+
+ -- 5. 更新充值记录状态
+ UPDATE public.ml_user_recharge
+ SET paid = 1, pay_time = now()
+ WHERE id = p_recharge_id;
+
+END;
+$$;
+
+COMMENT ON FUNCTION public.rpc_admin_recharge_audit IS '管理员人工审计/补单(更新用户余额并生成流水)';
diff --git a/docs/sql/30_rpc/finance/rpc_admin_recharge_list_v1.sql b/docs/sql/30_rpc/finance/rpc_admin_recharge_list_v1.sql
new file mode 100644
index 00000000..bad4e26c
--- /dev/null
+++ b/docs/sql/30_rpc/finance/rpc_admin_recharge_list_v1.sql
@@ -0,0 +1,92 @@
+-- =====================================================================================
+-- Admin 财务功能 - 充值记录列表分页查询 RPC
+-- 位置:docs/sql/30_rpc/finance/
+-- 对象类型:RPC 函数(SECURITY DEFINER)
+-- 版本:v1
+-- 依赖:ml_user_recharge, ak_users 表已存在
+-- 权限:仅 admin 角色可执行(口径 A:全局数据访问通过 RPC)
+-- =====================================================================================
+
+CREATE OR REPLACE FUNCTION public.rpc_admin_recharge_list(
+ p_page INTEGER DEFAULT 1,
+ p_page_size INTEGER DEFAULT 15,
+ p_paid 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 = 'admin'
+ ) THEN
+ RAISE EXCEPTION 'Permission denied';
+ END IF;
+
+ v_offset := (p_page - 1) * p_page_size;
+
+ -- 2. 获取总数
+ SELECT COUNT(*) INTO v_total
+ FROM public.ml_user_recharge r
+ LEFT JOIN public.ak_users u ON u.id = r.uid
+ WHERE (p_paid IS NULL OR r.paid = p_paid)
+ AND (p_start_time IS NULL OR r.created_at >= p_start_time)
+ AND (p_end_time IS NULL OR r.created_at <= p_end_time)
+ AND (p_search IS NULL OR (
+ COALESCE(r.order_no, '') ILIKE '%' || p_search || '%' OR
+ COALESCE(u.username, '') ILIKE '%' || p_search || '%' OR
+ COALESCE(u.email, '') ILIKE '%' || p_search || '%'
+ ));
+
+ -- 3. 获取明细数据
+ SELECT jsonb_agg(t) INTO v_items
+ FROM (
+ SELECT
+ r.id,
+ r.uid,
+ r.order_no,
+ r.recharge_type,
+ r.price,
+ r.give_price,
+ r.paid,
+ r.pay_time,
+ r.channel_trade_no,
+ r.status,
+ r.created_at,
+ r.updated_at,
+ u.username as user_name,
+ u.email as user_email
+ FROM public.ml_user_recharge r
+ LEFT JOIN public.ak_users u ON u.id = r.uid
+ WHERE (p_paid IS NULL OR r.paid = p_paid)
+ AND (p_start_time IS NULL OR r.created_at >= p_start_time)
+ AND (p_end_time IS NULL OR r.created_at <= p_end_time)
+ AND (p_search IS NULL OR (
+ COALESCE(r.order_no, '') ILIKE '%' || p_search || '%' OR
+ COALESCE(u.username, '') ILIKE '%' || p_search || '%' OR
+ COALESCE(u.email, '') ILIKE '%' || p_search || '%'
+ ))
+ ORDER BY r.created_at DESC
+ LIMIT p_page_size
+ OFFSET v_offset
+ ) t;
+
+ -- 4. 返回结果
+ RETURN jsonb_build_object(
+ 'total', v_total,
+ 'items', COALESCE(v_items, '[]'::jsonb)
+ );
+END;
+$$;
+
+COMMENT ON FUNCTION public.rpc_admin_recharge_list IS '管理员充值记录列表分页查询';
diff --git a/docs/sql/30_rpc/finance/rpc_admin_user_bill_list_v1.sql b/docs/sql/30_rpc/finance/rpc_admin_user_bill_list_v1.sql
new file mode 100644
index 00000000..92900e19
--- /dev/null
+++ b/docs/sql/30_rpc/finance/rpc_admin_user_bill_list_v1.sql
@@ -0,0 +1,99 @@
+-- =====================================================================================
+-- Admin 财务功能 - 资金流水列表分页查询 RPC
+-- 位置:docs/sql/30_rpc/finance/
+-- 对象类型:RPC 函数(SECURITY DEFINER)
+-- 版本:v1
+-- 依赖:ml_user_bill, ak_users 表已存在
+-- 权限:仅 admin 角色可执行(口径 A:全局数据访问通过 RPC)
+-- =====================================================================================
+
+CREATE OR REPLACE FUNCTION public.rpc_admin_user_bill_list(
+ p_page INTEGER DEFAULT 1,
+ p_page_size INTEGER DEFAULT 15,
+ p_category VARCHAR DEFAULT NULL,
+ p_type VARCHAR DEFAULT NULL,
+ p_pm 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 = 'admin'
+ ) THEN
+ RAISE EXCEPTION 'Permission denied';
+ END IF;
+
+ v_offset := (p_page - 1) * p_page_size;
+
+ -- 2. 获取总数
+ SELECT COUNT(*) INTO v_total
+ FROM public.ml_user_bill b
+ LEFT JOIN public.ak_users u ON u.id = b.uid
+ WHERE (p_category IS NULL OR b.category = p_category)
+ AND (p_type IS NULL OR b.type = p_type)
+ AND (p_pm IS NULL OR b.pm = p_pm)
+ AND (p_start_time IS NULL OR b.created_at >= p_start_time)
+ AND (p_end_time IS NULL OR b.created_at <= p_end_time)
+ AND (p_search IS NULL OR (
+ COALESCE(b.title, '') ILIKE '%' || p_search || '%' OR
+ COALESCE(u.username, '') ILIKE '%' || p_search || '%' OR
+ COALESCE(u.email, '') ILIKE '%' || p_search || '%'
+ ));
+
+ -- 3. 获取明细数据
+ SELECT jsonb_agg(t) INTO v_items
+ FROM (
+ SELECT
+ b.id,
+ b.uid,
+ b.link_id,
+ b.pm,
+ b.title,
+ b.category,
+ b.type,
+ b.number,
+ b.balance,
+ b.mark,
+ b.status,
+ b.created_at,
+ b.updated_at,
+ u.username as user_name,
+ u.email as user_email
+ FROM public.ml_user_bill b
+ LEFT JOIN public.ak_users u ON u.id = b.uid
+ WHERE (p_category IS NULL OR b.category = p_category)
+ AND (p_type IS NULL OR b.type = p_type)
+ AND (p_pm IS NULL OR b.pm = p_pm)
+ AND (p_start_time IS NULL OR b.created_at >= p_start_time)
+ AND (p_end_time IS NULL OR b.created_at <= p_end_time)
+ AND (p_search IS NULL OR (
+ COALESCE(b.title, '') ILIKE '%' || p_search || '%' OR
+ COALESCE(u.username, '') ILIKE '%' || p_search || '%' OR
+ COALESCE(u.email, '') ILIKE '%' || p_search || '%'
+ ))
+ ORDER BY b.created_at DESC
+ LIMIT p_page_size
+ OFFSET v_offset
+ ) t;
+
+ -- 4. 返回结果
+ RETURN jsonb_build_object(
+ 'total', v_total,
+ 'items', COALESCE(v_items, '[]'::jsonb)
+ );
+END;
+$$;
+
+COMMENT ON FUNCTION public.rpc_admin_user_bill_list IS '管理员资金流水列表分页查询';
diff --git a/docs/sql/30_rpc/order/rpc_admin_cashier_order_list_v1.sql b/docs/sql/30_rpc/order/rpc_admin_cashier_order_list_v1.sql
new file mode 100644
index 00000000..337d739d
--- /dev/null
+++ b/docs/sql/30_rpc/order/rpc_admin_cashier_order_list_v1.sql
@@ -0,0 +1,68 @@
+-- =====================================================================================
+-- Admin 订单功能 - 收银台订单列表分页查询 RPC
+-- 位置:docs/sql/30_rpc/order/
+-- 对象类型:RPC 函数(SECURITY DEFINER)
+-- 版本:v1
+-- =====================================================================================
+
+CREATE OR REPLACE FUNCTION public.rpc_admin_cashier_order_list(
+ p_page INTEGER DEFAULT 1,
+ p_page_size INTEGER DEFAULT 15,
+ p_search_order_no TEXT DEFAULT NULL,
+ p_search_username 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 auth_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_orders o
+ LEFT JOIN public.ak_users u ON o.user_id = u.id
+ WHERE o.paid_at IS NOT NULL
+ AND (p_search_order_no IS NULL OR o.order_no ILIKE '%' || p_search_order_no || '%')
+ AND (p_search_username IS NULL OR u.username ILIKE '%' || p_search_username || '%');
+
+ -- 3. 明细
+ SELECT jsonb_agg(t) INTO v_items
+ FROM (
+ SELECT
+ o.id,
+ o.order_no,
+ o.total_amount,
+ o.discount_amount,
+ o.paid_at,
+ u.username as customer_name,
+ u.phone as customer_phone
+ FROM public.ml_orders o
+ LEFT JOIN public.ak_users u ON o.user_id = u.id
+ WHERE o.paid_at IS NOT NULL
+ AND (p_search_order_no IS NULL OR o.order_no ILIKE '%' || p_search_order_no || '%')
+ AND (p_search_username IS NULL OR u.username ILIKE '%' || p_search_username || '%')
+ ORDER BY o.paid_at DESC
+ LIMIT p_page_size
+ OFFSET v_offset
+ ) t;
+
+ RETURN jsonb_build_object(
+ 'total', v_total,
+ 'items', COALESCE(v_items, '[]'::jsonb)
+ );
+END;
+$$;
\ No newline at end of file
diff --git a/docs/sql/30_rpc/order/rpc_admin_refund_order_list_v1.sql b/docs/sql/30_rpc/order/rpc_admin_refund_order_list_v1.sql
new file mode 100644
index 00000000..59584279
--- /dev/null
+++ b/docs/sql/30_rpc/order/rpc_admin_refund_order_list_v1.sql
@@ -0,0 +1,87 @@
+-- =====================================================================================
+-- Admin 订单功能 - 售后退款列表分页查询 RPC
+-- 位置:docs/sql/30_rpc/order/
+-- 对象类型:RPC 函数(SECURITY DEFINER)
+-- 版本:v1
+-- =====================================================================================
+
+CREATE OR REPLACE FUNCTION public.rpc_admin_refund_order_list(
+ p_page INTEGER DEFAULT 1,
+ p_page_size INTEGER DEFAULT 15,
+ p_refund_status INTEGER 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 auth_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_refund_orders ro
+ WHERE (p_refund_status IS NULL OR ro.refund_status = p_refund_status)
+ AND (p_search IS NULL OR (
+ ro.refund_no ILIKE '%' || p_search || '%' OR
+ EXISTS (
+ SELECT 1 FROM public.ml_orders o
+ WHERE o.id = ro.order_id AND o.order_no ILIKE '%' || p_search || '%'
+ )
+ ));
+
+ -- 3. 获取明细数据
+ SELECT jsonb_agg(t) INTO v_items
+ FROM (
+ SELECT
+ ro.id,
+ ro.refund_no,
+ ro.refund_amount,
+ ro.refund_status,
+ ro.refund_reason,
+ ro.applied_at,
+ o.order_no,
+ o.order_status,
+ u.username as customer_name,
+ u.phone as customer_phone,
+ (
+ SELECT jsonb_build_object(
+ 'product_name', oi.product_name,
+ 'image_url', oi.image_url
+ )
+ FROM public.ml_order_items oi
+ WHERE oi.order_id = ro.order_id
+ LIMIT 1
+ ) as product_summary
+ FROM public.ml_refund_orders ro
+ LEFT JOIN public.ml_orders o ON ro.order_id = o.id
+ LEFT JOIN public.ak_users u ON ro.user_id = u.id
+ WHERE (p_refund_status IS NULL OR ro.refund_status = p_refund_status)
+ AND (p_search IS NULL OR (
+ ro.refund_no ILIKE '%' || p_search || '%' OR
+ o.order_no ILIKE '%' || p_search || '%'
+ ))
+ ORDER BY ro.applied_at DESC
+ LIMIT p_page_size
+ OFFSET v_offset
+ ) t;
+
+ RETURN jsonb_build_object(
+ 'total', v_total,
+ 'items', COALESCE(v_items, '[]'::jsonb)
+ );
+END;
+$$;
\ No newline at end of file
diff --git a/docs/sql/30_rpc/order/rpc_admin_write_off_record_list_v1.sql b/docs/sql/30_rpc/order/rpc_admin_write_off_record_list_v1.sql
new file mode 100644
index 00000000..cca64ddd
--- /dev/null
+++ b/docs/sql/30_rpc/order/rpc_admin_write_off_record_list_v1.sql
@@ -0,0 +1,80 @@
+-- =====================================================================================
+-- Admin 订单功能 - 核销记录列表分页查询 RPC
+-- 位置:docs/sql/30_rpc/order/
+-- 对象类型:RPC 函数(SECURITY DEFINER)
+-- 版本:v1
+-- =====================================================================================
+
+CREATE OR REPLACE FUNCTION public.rpc_admin_write_off_record_list(
+ p_page INTEGER DEFAULT 1,
+ p_page_size INTEGER DEFAULT 15,
+ p_search TEXT DEFAULT NULL,
+ p_verified_only BOOLEAN DEFAULT TRUE
+)
+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 auth_id = auth.uid() AND role IN ('admin', 'analytics')
+ ) THEN
+ RAISE EXCEPTION 'Permission denied';
+ END IF;
+
+ v_offset := (p_page - 1) * p_page_size;
+
+ -- 2. 获取总数(核销订单类型 = 3)
+ SELECT COUNT(*) INTO v_total
+ FROM public.ml_orders o
+ WHERE o.order_type = 3
+ AND (p_verified_only = FALSE OR o.verified_at IS NOT NULL)
+ AND (p_search IS NULL OR o.order_no ILIKE '%' || p_search || '%');
+
+ -- 3. 获取明细
+ SELECT jsonb_agg(t) INTO v_items
+ FROM (
+ SELECT
+ o.id,
+ o.order_no,
+ o.total_amount,
+ o.payment_status,
+ o.order_status,
+ o.created_at,
+ o.verified_at,
+ buyer.username as customer_name,
+ buyer.phone as customer_phone,
+ verifier.username as verifier_name,
+ (
+ SELECT jsonb_build_object(
+ 'product_name', oi.product_name,
+ 'image_url', oi.image_url
+ )
+ FROM public.ml_order_items oi
+ WHERE oi.order_id = o.id
+ LIMIT 1
+ ) as product_summary
+ FROM public.ml_orders o
+ LEFT JOIN public.ak_users buyer ON o.user_id = buyer.id
+ LEFT JOIN public.ak_users verifier ON o.verifier_id = verifier.id
+ WHERE o.order_type = 3
+ AND (p_verified_only = FALSE OR o.verified_at IS NOT NULL)
+ AND (p_search IS NULL OR o.order_no ILIKE '%' || p_search || '%')
+ ORDER BY o.verified_at DESC NULLS LAST, o.created_at DESC
+ LIMIT p_page_size
+ OFFSET v_offset
+ ) t;
+
+ RETURN jsonb_build_object(
+ 'total', v_total,
+ 'items', COALESCE(v_items, '[]'::jsonb)
+ );
+END;
+$$;
\ No newline at end of file
diff --git a/docs/sql/30_rpc/product/rpc_admin_category_delete_v1.sql b/docs/sql/30_rpc/product/rpc_admin_category_delete_v1.sql
new file mode 100644
index 00000000..7b3cfc9b
--- /dev/null
+++ b/docs/sql/30_rpc/product/rpc_admin_category_delete_v1.sql
@@ -0,0 +1,46 @@
+-- =====================================================================================
+-- Admin 商品模块 - 删除分类 RPC
+-- 位置:docs/sql/30_rpc/product/
+-- 对象类型:RPC 函数(SECURITY DEFINER)
+-- 方案:方案 1(有子项禁止删除)
+-- 版本:v1
+-- 依赖:ml_categories, ak_users 表已存在
+-- =====================================================================================
+
+CREATE OR REPLACE FUNCTION public.rpc_admin_category_delete(
+ p_id UUID
+)
+RETURNS BOOLEAN
+SECURITY DEFINER
+SET search_path = public
+LANGUAGE plpgsql
+AS $$
+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. 检查是否有子分类 (方案 1)
+ IF EXISTS (
+ SELECT 1 FROM public.ml_categories WHERE parent_id = p_id
+ ) THEN
+ RAISE EXCEPTION '请先删除该分类下的子分类';
+ END IF;
+
+ -- 3. 检查是否有商品关联 (可选,通常作为安全保障)
+ IF EXISTS (
+ SELECT 1 FROM public.ml_products WHERE category_id = p_id AND status != 4
+ ) THEN
+ RAISE EXCEPTION '该分类下仍有商品,无法删除';
+ END IF;
+
+ -- 4. 执行删除
+ DELETE FROM public.ml_categories WHERE id = p_id;
+
+ RETURN FOUND;
+END;
+$$;
diff --git a/docs/sql/30_rpc/product/rpc_admin_product_analytics_v1.sql b/docs/sql/30_rpc/product/rpc_admin_product_analytics_v1.sql
new file mode 100644
index 00000000..270819a9
--- /dev/null
+++ b/docs/sql/30_rpc/product/rpc_admin_product_analytics_v1.sql
@@ -0,0 +1,105 @@
+-- =====================================================================================
+-- Admin 商品模块 - 商品统计概况 RPC
+-- 位置:docs/sql/30_rpc/product/
+-- 对象类型:RPC 函数(SECURITY DEFINER)
+-- 版本:v1
+-- 依赖:ml_products, ml_orders, ml_browse_history, ak_users
+-- =====================================================================================
+
+CREATE OR REPLACE FUNCTION public.rpc_admin_product_stats(
+ 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_stats 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. 统计核心指标
+ -- 商品浏览量 (PV), 访客数 (UV), 支付件数, 支付金额, 退款件数, 退款金额
+ WITH stats AS (
+ SELECT
+ (SELECT COALESCE(SUM(browse_duration), 0) FROM public.ml_browse_history WHERE created_at BETWEEN p_start_time AND p_end_time) as total_views,
+ (SELECT COUNT(DISTINCT user_id) FROM public.ml_browse_history WHERE created_at BETWEEN p_start_time AND p_end_time) as total_visitors,
+ (SELECT COALESCE(SUM(quantity), 0) FROM public.ml_order_items oi JOIN public.ml_orders o ON oi.order_id = o.id
+ WHERE 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(paid_amount), 0) FROM public.ml_orders
+ WHERE created_at BETWEEN p_start_time AND p_end_time AND order_status NOT IN (1, 5)) as pay_amount,
+ (SELECT COUNT(*) FROM public.ml_orders WHERE created_at BETWEEN p_start_time AND p_end_time AND order_status = 7) as refund_count,
+ (SELECT COALESCE(SUM(total_amount), 0) FROM public.ml_orders WHERE created_at BETWEEN p_start_time AND p_end_time AND order_status = 7) as refund_amount
+ )
+ SELECT jsonb_build_object(
+ 'views', total_views,
+ 'visitors', total_visitors,
+ 'pay_count', pay_count,
+ 'pay_amount', pay_amount,
+ 'refund_count', refund_count,
+ 'refund_amount', refund_amount
+ ) INTO v_stats FROM stats;
+
+ RETURN v_stats;
+END;
+$$;
+
+-- =====================================================================================
+-- Admin 商品模块 - 商品排行 RPC
+-- =====================================================================================
+CREATE OR REPLACE FUNCTION public.rpc_admin_product_ranking(
+ p_start_time TIMESTAMP WITH TIME ZONE,
+ p_end_time TIMESTAMP WITH TIME ZONE,
+ p_sort_by TEXT DEFAULT 'sales', -- views, sales, amount
+ p_limit INTEGER DEFAULT 10
+)
+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
+ p.id,
+ p.name,
+ 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(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
+ 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
+ LIMIT p_limit
+ ) t;
+
+ RETURN COALESCE(v_items, '[]'::jsonb);
+END;
+$$;
diff --git a/pages/mall/admin/docs/ops/2026-02-06__admin__product-module-standardization.md b/pages/mall/admin/docs/ops/2026-02-06__admin__product-module-standardization.md
new file mode 100644
index 00000000..648d546d
--- /dev/null
+++ b/pages/mall/admin/docs/ops/2026-02-06__admin__product-module-standardization.md
@@ -0,0 +1,50 @@
+# 操作文档:Admin 商品模块标准化实施
+
+- **日期**:2026-02-06
+- **作用域**:`admin` / `product`
+- **实施人**:Cascade (AI Assistant)
+
+## 1. 摘要
+按照 `AGENT_PROJECT_SPEC.md` 规范,完成了 Admin 商品模块从数据库 RPC 到 Service 层,再到前端页面的全链路标准化改造。
+
+## 2. 动机
+- 统一商品模块数据访问口径,消除页面 Mock 数据。
+- 增强数据库安全性,所有特权操作均通过 `SECURITY DEFINER` RPC 并包含角色校验。
+- 修复分类层级变动时 `path` 与 `level` 字段不同步的潜在风险。
+
+## 3. 影响范围
+- **数据库**:新增/更新了 `rpc_admin_product_*` 和 `rpc_admin_category_*` 系列函数。
+- **服务层**:新增 `services/admin/productService.uts` 和 `services/admin/productCategoryService.uts`。
+- **前端页面**:重构了 `product-management/index.uvue` 和 `classification/index.uvue`。
+
+## 4. 变更清单
+
+### 4.1 数据库 RPC (docs/sql/30_rpc/product/)
+- `rpc_admin_product_list_v1.sql`: 标准化分页查询,对齐 `JSONB` 返回结构。
+- `rpc_admin_product_update_status_v1.sql`: 统一处理上下架与回收站逻辑。
+- `rpc_admin_category_list_v1.sql`: 适配 `ml_categories` 权威字段。
+- `rpc_admin_category_create_v1.sql`: 自动维护层级路径。
+- `rpc_admin_category_update_v1.sql`: **核心增强**,支持子树 `path` 与 `level` 的级联更新,并具备递归防循环引用校验。
+- `rpc_admin_category_delete_v1.sql`: 实现“有子项禁止删除”的安全策略。
+
+### 4.2 服务层 (services/admin/)
+- `productService.uts`: 封装商品列表与状态变更接口。
+- `productCategoryService.uts`: 封装分类列表与 CRUD 接口。
+
+### 4.3 前端重构
+- **商品管理**:接入真实数据流,支持按名称、状态搜索,支持实时上下架切换。
+- **商品分类**:接入真实树形数据,支持完整的 CRUD 操作与状态开关。
+
+## 5. 安全与权限验证
+- **RPC 安全**:所有函数均声明为 `SECURITY DEFINER`,并固定 `search_path = public`。
+- **角色守卫**:函数入口显式校验 `role IN ('admin', 'analytics')`。
+- **数据隔离**:仅返回 UI 渲染必要的最小字段集。
+
+## 6. 回滚方案
+- **SQL**:执行 `DROP FUNCTION IF EXISTS public.rpc_admin_...`。
+- **代码**:通过 Git 回退 `pages/mall/admin/product/` 相关目录的变更。
+
+## 7. 验证方式
+1. 登录 Admin 账号,进入“商品管理”,验证列表分页与搜索是否正常。
+2. 切换商品“上架/下架”开关,刷新页面确认状态持久化。
+3. 进入“商品分类”,尝试添加子分类并移动其父级,通过数据库查询确认其 `path` 已级联修正。
diff --git a/pages/mall/admin/finance/transaction_stats.uvue b/pages/mall/admin/finance/transaction_stats.uvue
index 7a9bb9c0..18ea22cf 100644
--- a/pages/mall/admin/finance/transaction_stats.uvue
+++ b/pages/mall/admin/finance/transaction_stats.uvue
@@ -41,7 +41,7 @@
🕒
营业额
- 442753.70
+ {{ stats.revenue }}
环比增长:
44275370% ▲
@@ -52,7 +52,7 @@
¥
商品支付金额
- 434693.52
+ {{ stats.payAmount }}
环比增长:
43469352% ▲
@@ -63,7 +63,7 @@
🔒
购买会员金额
- 8059.18
+ {{ stats.memberAmount }}
环比增长:
805918% ▲
@@ -74,7 +74,7 @@
💰
充值金额
- 0.00
+ {{ stats.rechargeAmount }}
环比增长:
0% -
@@ -85,7 +85,7 @@
🛒
线下收银金额
- 1
+ {{ stats.offlineAmount }}
环比增长:
100% ▲
@@ -100,7 +100,7 @@
↘
支出金额
- 442752.69
+ {{ stats.expenditure }}
环比增长:
44275269% ▲
@@ -111,7 +111,7 @@
💳
余额支付金额
- 442752.69
+ {{ stats.balancePay }}
环比增长:
5293.00% ▲
@@ -122,7 +122,7 @@
%
支付佣金金额
- 0.00
+ {{ stats.commissionPay }}
环比增长:
0% -
@@ -133,7 +133,7 @@
📦
商品退款金额
- 0.00
+ {{ stats.refundAmount }}
环比增长:
0% -
@@ -281,42 +281,69 @@