feat(admin): merge stash changes into comclib-analytics (order/finance/product + rpc sql)

This commit is contained in:
comlibmb
2026-02-10 18:49:21 +08:00
parent bf394eb65d
commit 80e5a1ddeb
23 changed files with 1599 additions and 143 deletions

View File

@@ -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 '管理员提现申请列表分页查询';

View File

@@ -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通过时扣款';

View File

@@ -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 '管理员人工审计/补单(更新用户余额并生成流水)';

View File

@@ -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 '管理员充值记录列表分页查询';

View File

@@ -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 '管理员资金流水列表分页查询';