Initial commit of akmon project
This commit is contained in:
229
fix_triggers.sql
Normal file
229
fix_triggers.sql
Normal file
@@ -0,0 +1,229 @@
|
||||
-- =====================================================
|
||||
-- 修复触发器冲突脚本
|
||||
-- 解决 "trigger already exists" 错误
|
||||
-- =====================================================
|
||||
|
||||
-- 清理所有可能存在的触发器
|
||||
DO $$
|
||||
BEGIN
|
||||
-- 删除用户角色相关触发器
|
||||
DROP TRIGGER IF EXISTS trigger_update_user_roles_updated_at ON public.user_roles;
|
||||
DROP TRIGGER IF EXISTS on_auth_user_created ON auth.users;
|
||||
|
||||
-- 删除可能存在的旧版本触发器
|
||||
DROP TRIGGER IF EXISTS update_user_roles_updated_at ON public.user_roles;
|
||||
DROP TRIGGER IF EXISTS handle_new_user_trigger ON auth.users;
|
||||
|
||||
RAISE NOTICE '✅ 已清理所有现有触发器';
|
||||
END $$;
|
||||
|
||||
-- 清理可能存在的函数冲突
|
||||
DO $$
|
||||
BEGIN
|
||||
-- 删除函数(CASCADE 会自动删除依赖的触发器)
|
||||
DROP FUNCTION IF EXISTS public.update_user_roles_updated_at() CASCADE;
|
||||
DROP FUNCTION IF EXISTS public.handle_new_user() CASCADE;
|
||||
DROP FUNCTION IF EXISTS public.update_user_role(UUID, TEXT, UUID) CASCADE;
|
||||
DROP FUNCTION IF EXISTS public.get_user_role(UUID) CASCADE;
|
||||
DROP FUNCTION IF EXISTS public.batch_update_user_roles(JSONB, UUID) CASCADE;
|
||||
DROP FUNCTION IF EXISTS public.sync_user_role_metadata(UUID) CASCADE;
|
||||
|
||||
RAISE NOTICE '✅ 已清理可能冲突的函数';
|
||||
END $$;
|
||||
|
||||
-- 重新创建核心函数
|
||||
-- 1. 更新时间戳函数
|
||||
CREATE OR REPLACE FUNCTION public.update_user_roles_updated_at()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = NOW();
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- 2. 同步用户角色到元数据的函数
|
||||
CREATE OR REPLACE FUNCTION public.sync_user_role_metadata(target_user_id UUID)
|
||||
RETURNS BOOLEAN AS $$
|
||||
DECLARE
|
||||
user_role_record RECORD;
|
||||
BEGIN
|
||||
-- 获取用户角色信息
|
||||
SELECT role, class_id, school_id, department, permissions, is_active
|
||||
INTO user_role_record
|
||||
FROM public.user_roles
|
||||
WHERE user_id = target_user_id AND is_active = true;
|
||||
|
||||
IF FOUND THEN
|
||||
-- 更新 auth.users 的元数据
|
||||
UPDATE auth.users
|
||||
SET raw_user_meta_data = COALESCE(raw_user_meta_data, '{}'::jsonb) ||
|
||||
jsonb_build_object(
|
||||
'user_role', user_role_record.role,
|
||||
'class_id', user_role_record.class_id,
|
||||
'school_id', user_role_record.school_id,
|
||||
'department', user_role_record.department,
|
||||
'permissions', user_role_record.permissions,
|
||||
'role_synced_at', extract(epoch from now())
|
||||
)
|
||||
WHERE id = target_user_id;
|
||||
|
||||
RETURN true;
|
||||
ELSE
|
||||
-- 用户没有活跃角色,清除角色信息
|
||||
UPDATE auth.users
|
||||
SET raw_user_meta_data = COALESCE(raw_user_meta_data, '{}'::jsonb) ||
|
||||
jsonb_build_object(
|
||||
'user_role', 'inactive',
|
||||
'role_synced_at', extract(epoch from now())
|
||||
)
|
||||
WHERE id = target_user_id;
|
||||
|
||||
RETURN false;
|
||||
END IF;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
||||
|
||||
-- 3. 自动为新用户分配角色的函数
|
||||
CREATE OR REPLACE FUNCTION public.handle_new_user()
|
||||
RETURNS TRIGGER AS $$
|
||||
DECLARE
|
||||
user_role TEXT := 'student'; -- 默认角色
|
||||
user_email TEXT := NEW.email;
|
||||
user_domain TEXT;
|
||||
BEGIN
|
||||
-- 提取邮箱域名
|
||||
user_domain := split_part(user_email, '@', 2);
|
||||
|
||||
-- 根据邮箱域名或特定规则分配角色
|
||||
CASE
|
||||
WHEN user_domain IN ('teacher.edu', 'faculty.edu', 'staff.edu') THEN
|
||||
user_role := 'teacher';
|
||||
WHEN user_domain IN ('admin.edu', 'management.edu') THEN
|
||||
user_role := 'admin';
|
||||
WHEN user_email LIKE '%admin%' OR user_email LIKE '%manager%' THEN
|
||||
user_role := 'admin';
|
||||
WHEN user_email LIKE '%teacher%' OR user_email LIKE '%faculty%' THEN
|
||||
user_role := 'teacher';
|
||||
ELSE
|
||||
user_role := 'student';
|
||||
END CASE;
|
||||
|
||||
-- 插入角色记录
|
||||
INSERT INTO public.user_roles (user_id, role, created_by)
|
||||
VALUES (NEW.id, user_role, NEW.id);
|
||||
|
||||
-- 同步到元数据
|
||||
PERFORM public.sync_user_role_metadata(NEW.id);
|
||||
|
||||
RAISE NOTICE '✅ 为用户 % 分配角色: %', NEW.email, user_role;
|
||||
|
||||
RETURN NEW;
|
||||
EXCEPTION WHEN OTHERS THEN
|
||||
RAISE WARNING '⚠️ 为用户 % 分配角色失败: %', NEW.email, SQLERRM;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
||||
|
||||
-- 4. 获取用户角色的函数(支持缓存)
|
||||
CREATE OR REPLACE FUNCTION public.get_user_role(target_user_id UUID DEFAULT auth.uid())
|
||||
RETURNS TEXT AS $$
|
||||
DECLARE
|
||||
user_role TEXT;
|
||||
cached_role TEXT;
|
||||
role_synced_at NUMERIC;
|
||||
BEGIN
|
||||
-- 如果没有用户ID,返回匿名
|
||||
IF target_user_id IS NULL THEN
|
||||
RETURN 'anonymous';
|
||||
END IF;
|
||||
|
||||
-- 尝试从 auth.users 元数据获取缓存角色
|
||||
SELECT
|
||||
raw_user_meta_data->>'user_role',
|
||||
(raw_user_meta_data->>'role_synced_at')::numeric
|
||||
INTO cached_role, role_synced_at
|
||||
FROM auth.users
|
||||
WHERE id = target_user_id;
|
||||
|
||||
-- 如果缓存新鲜(5分钟内),直接返回
|
||||
IF cached_role IS NOT NULL AND role_synced_at IS NOT NULL
|
||||
AND (extract(epoch from now()) - role_synced_at) < 300 THEN
|
||||
RETURN cached_role;
|
||||
END IF;
|
||||
|
||||
-- 从角色表获取最新角色
|
||||
SELECT role INTO user_role
|
||||
FROM public.user_roles
|
||||
WHERE user_id = target_user_id AND is_active = true;
|
||||
|
||||
-- 如果找到角色,同步到元数据
|
||||
IF FOUND THEN
|
||||
PERFORM public.sync_user_role_metadata(target_user_id);
|
||||
RETURN user_role;
|
||||
ELSE
|
||||
RETURN 'student'; -- 默认角色
|
||||
END IF;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
||||
|
||||
-- 重新创建触发器
|
||||
DO $$
|
||||
BEGIN
|
||||
-- 创建更新时间戳触发器
|
||||
CREATE TRIGGER trigger_update_user_roles_updated_at
|
||||
BEFORE UPDATE ON public.user_roles
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION public.update_user_roles_updated_at();
|
||||
|
||||
-- 创建新用户触发器
|
||||
CREATE TRIGGER on_auth_user_created
|
||||
AFTER INSERT ON auth.users
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION public.handle_new_user();
|
||||
|
||||
RAISE NOTICE '✅ 触发器重建完成';
|
||||
END $$;
|
||||
|
||||
-- 验证触发器是否正确创建
|
||||
DO $$
|
||||
DECLARE
|
||||
trigger_count INTEGER;
|
||||
BEGIN
|
||||
SELECT COUNT(*) INTO trigger_count
|
||||
FROM information_schema.triggers
|
||||
WHERE trigger_schema = 'public'
|
||||
AND trigger_name IN ('trigger_update_user_roles_updated_at', 'on_auth_user_created');
|
||||
|
||||
IF trigger_count >= 2 THEN
|
||||
RAISE NOTICE '🎉 触发器验证通过: % 个触发器已创建', trigger_count;
|
||||
ELSE
|
||||
RAISE WARNING '⚠️ 触发器验证失败: 只找到 % 个触发器', trigger_count;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- 验证函数是否正确创建
|
||||
DO $$
|
||||
DECLARE
|
||||
function_count INTEGER;
|
||||
BEGIN
|
||||
SELECT COUNT(*) INTO function_count
|
||||
FROM information_schema.routines
|
||||
WHERE routine_schema = 'public'
|
||||
AND routine_name IN (
|
||||
'update_user_roles_updated_at',
|
||||
'handle_new_user',
|
||||
'sync_user_role_metadata',
|
||||
'get_user_role'
|
||||
);
|
||||
|
||||
IF function_count >= 4 THEN
|
||||
RAISE NOTICE '🎉 函数验证通过: % 个函数已创建', function_count;
|
||||
ELSE
|
||||
RAISE WARNING '⚠️ 函数验证失败: 只找到 % 个函数', function_count;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- 完成消息
|
||||
SELECT
|
||||
'🔧 触发器修复完成' as status,
|
||||
'现在可以安全地执行其他SQL脚本' as message;
|
||||
Reference in New Issue
Block a user