sql数据流,amdin业务逻辑接入

This commit is contained in:
comlibmb
2026-02-05 10:11:09 +08:00
parent 859372ca5b
commit ac670cf5d8
81 changed files with 3547 additions and 1472 deletions

0
docs/sql/30_rpc/.keep Normal file
View File

View File

View File

@@ -0,0 +1,41 @@
-- =====================================================================================
-- RPC: rpc_analytics_user_gender_distribution
-- Version: v1
-- Purpose: 统计指定周期内新增用户的性别分布(用于 Admin/Analytics 图表)
-- Security: SECURITY DEFINER + 固定 search_path + 入口角色鉴权
-- Depends: public.ak_users, public.get_current_user_role()
-- =====================================================================================
CREATE OR REPLACE FUNCTION public.rpc_analytics_user_gender_distribution(
p_start_date DATE,
p_end_date DATE
)
RETURNS TABLE (
name TEXT,
value BIGINT
)
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = public
AS $$
BEGIN
IF public.get_current_user_role() NOT IN ('admin', 'analytics') THEN
RAISE EXCEPTION 'Permission denied: required role admin or analytics';
END IF;
RETURN QUERY
SELECT
CASE
WHEN gender IS NULL OR TRIM(gender::text) = '' THEN '未知'
WHEN LOWER(TRIM(gender::text)) = 'male' THEN ''
WHEN LOWER(TRIM(gender::text)) = 'female' THEN ''
WHEN LOWER(TRIM(gender::text)) = 'other' THEN '未知'
ELSE '未知'
END AS name,
COUNT(*)::BIGINT AS value
FROM public.ak_users
WHERE created_at::DATE BETWEEN p_start_date AND p_end_date
GROUP BY 1
ORDER BY value DESC;
END;
$$;

View File

@@ -0,0 +1,19 @@
-- =====================================================================================
-- RPC: get_current_user_role
-- Version: v1
-- Purpose: 获取当前登录用户的角色(用于 RPC 入口鉴权)
-- Security: SECURITY DEFINER + 固定 search_path
-- Depends: public.ak_users (auth_id, role)
-- =====================================================================================
CREATE OR REPLACE FUNCTION public.get_current_user_role()
RETURNS TEXT
LANGUAGE sql
SECURITY DEFINER
SET search_path = public
AS $$
SELECT role
FROM public.ak_users
WHERE auth_id = auth.uid()
LIMIT 1;
$$;

View File

@@ -0,0 +1,76 @@
-- =====================================================================================
-- Trigger Function: handle_new_user
-- Version: v2
-- Purpose: auth.users 新用户创建后,同步写入 public.ak_users权威用户表并保持 user_roles 兼容写入
-- Security: SECURITY DEFINER + 固定 search_path
-- Depends:
-- - public.ak_users(auth_id,email,username,role)
-- - public.user_roles(user_id,role,created_by) (如存在)
-- Notes:
-- - 角色权威口径为 public.ak_users.role
-- - user_roles 为历史/兼容表:存在则写入,不存在则跳过
-- =====================================================================================
CREATE OR REPLACE FUNCTION public.handle_new_user()
RETURNS trigger
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = public
AS $$
DECLARE
user_role TEXT := 'customer';
user_email TEXT := NEW.email;
user_name TEXT;
has_user_roles BOOLEAN := FALSE;
BEGIN
-- 1) 基于邮箱规则分配默认角色(可按需调整)
IF user_email ILIKE '%@teacher.%' OR user_email ILIKE '%@edu.%' THEN
user_role := 'teacher';
ELSIF user_email ILIKE '%@admin.%' THEN
user_role := 'admin';
END IF;
-- 2) 默认 username取邮箱 @ 前缀
IF user_email IS NOT NULL AND POSITION('@' IN user_email) > 1 THEN
user_name := SPLIT_PART(user_email, '@', 1);
ELSE
user_name := 'user';
END IF;
-- 3) 写入 ak_users权威
-- 使用 ON CONFLICT 确保幂等:同一 auth_id 只会有一条记录
INSERT INTO public.ak_users (auth_id, email, username, role)
VALUES (NEW.id, user_email, user_name, user_role)
ON CONFLICT (auth_id)
DO UPDATE SET
email = COALESCE(EXCLUDED.email, public.ak_users.email),
username = COALESCE(EXCLUDED.username, public.ak_users.username),
role = COALESCE(public.ak_users.role, EXCLUDED.role),
updated_at = now();
-- 4) 兼容写入 user_roles如果表存在
SELECT EXISTS (
SELECT 1
FROM information_schema.tables
WHERE table_schema='public'
AND table_name='user_roles'
) INTO has_user_roles;
IF has_user_roles THEN
BEGIN
INSERT INTO public.user_roles (user_id, role, created_by)
VALUES (NEW.id, user_role, NEW.id);
EXCEPTION WHEN unique_violation THEN
-- 忽略重复
NULL;
END;
END IF;
-- 5) 更新 auth.users 元数据(可选保留)
UPDATE auth.users
SET raw_user_meta_data = COALESCE(raw_user_meta_data, '{}'::jsonb) || jsonb_build_object('user_role', user_role)
WHERE id = NEW.id;
RETURN NEW;
END;
$$;

View File

@@ -0,0 +1,81 @@
-- =====================================================================================
-- Trigger Function: handle_new_user
-- Version: v3
-- Purpose: auth.users 新用户创建后,同步写入 public.ak_users权威和 public.user_roles兼容
-- 此版本修复了向 user_roles 写入时可能因 role 为 NULL 导致的 NOT NULL 约束失败问题。
-- Security: SECURITY DEFINER + 固定 search_path
-- Depends:
-- - public.ak_users(auth_id,email,username,role)
-- - public.user_roles(user_id,role)
-- =====================================================================================
CREATE OR REPLACE FUNCTION public.handle_new_user()
RETURNS trigger
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = public
AS $$
DECLARE
user_role TEXT;
user_email TEXT := NEW.email;
user_name TEXT;
has_user_roles BOOLEAN := FALSE;
BEGIN
-- 1) 基于邮箱规则分配默认角色(可按需调整)
-- 确保 user_role 总有一个非 NULL 的值
user_role := CASE
WHEN user_email ILIKE '%@admin.%' THEN 'admin'
WHEN user_email ILIKE '%@teacher.%' OR user_email ILIKE '%@edu.%' THEN 'teacher'
ELSE 'consumer' -- 默认角色
END;
-- 2) 默认 username取邮箱 @ 前缀
IF user_email IS NOT NULL AND POSITION('@' IN user_email) > 1 THEN
user_name := SPLIT_PART(user_email, '@', 1);
ELSE
user_name := 'user_' || SUBSTRING(NEW.id::text, 1, 8); -- 使用 user_ + uid前8位作为备用名
END IF;
-- 3) 写入 ak_users权威
-- 使用 ON CONFLICT 确保幂等:同一 auth_id 只会有一条记录
INSERT INTO public.ak_users (auth_id, email, username, role)
VALUES (NEW.id, user_email, user_name, user_role)
ON CONFLICT (auth_id)
DO UPDATE SET
email = COALESCE(EXCLUDED.email, public.ak_users.email),
username = COALESCE(EXCLUDED.username, public.ak_users.username),
-- 只有当现有 role 为空时才更新,避免覆盖手动设置的 admin 角色
role = COALESCE(public.ak_users.role, EXCLUDED.role),
updated_at = now();
-- 4) 兼容写入 user_roles如果表存在
SELECT EXISTS (
SELECT 1
FROM information_schema.tables
WHERE table_schema='public'
AND table_name='user_roles'
) INTO has_user_roles;
IF has_user_roles THEN
BEGIN
-- 确保插入的 role 不为 NULL即使上面的逻辑有误
INSERT INTO public.user_roles (user_id, role, created_by)
VALUES (NEW.id, COALESCE(user_role, 'customer'), NEW.id);
EXCEPTION
WHEN unique_violation THEN
-- 忽略重复插入的错误
NULL;
WHEN not_null_violation THEN
-- 记录非空约束错误,但不中断整个触发器
RAISE NOTICE '[handle_new_user] WARNING: Failed to INSERT into user_roles due to NOT NULL violation. user_id: %, role: %', NEW.id, user_role;
END;
END IF;
-- 5) 更新 auth.users 元数据(可选保留)
UPDATE auth.users
SET raw_user_meta_data = COALESCE(raw_user_meta_data, '{}'::jsonb) || jsonb_build_object('user_role', user_role)
WHERE id = NEW.id;
RETURN NEW;
END;
$$;