Files
medical-mall/docs/sql/30_rpc/auth/handle_new_user_v3.sql
2026-02-05 10:11:09 +08:00

81 lines
3.0 KiB
PL/PgSQL
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
-- =====================================================================================
-- 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;
$$;