sql数据流,amdin业务逻辑接入
This commit is contained in:
0
docs/sql/00_meta/.keep
Normal file
0
docs/sql/00_meta/.keep
Normal file
9
docs/sql/00_meta/README.md
Normal file
9
docs/sql/00_meta/README.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# docs/sql/00_meta
|
||||
|
||||
本目录存放:
|
||||
- 规范/策略/说明类文档
|
||||
- 角色与权限策略
|
||||
- RPC 安全模型
|
||||
- 其他元信息
|
||||
|
||||
**注意**:本目录不包含 DDL/RLS/RPC/GRANT 等可执行对象,仅用于策略与说明。
|
||||
0
docs/sql/10_schema/.keep
Normal file
0
docs/sql/10_schema/.keep
Normal file
8
docs/sql/10_schema/README.md
Normal file
8
docs/sql/10_schema/README.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# docs/sql/10_schema
|
||||
|
||||
本目录存放:
|
||||
- 表/类型/索引等 DDL
|
||||
- 按域分组(如 `analytics/`、`user/`、`order/` 等)
|
||||
- 文件命名:`<object>_v<version>.sql`
|
||||
|
||||
**禁止**:在此目录混入 RLS、RPC、GRANT 等对象。
|
||||
19
docs/sql/10_schema/user/ak_users_constraints_fix_v1.sql
Normal file
19
docs/sql/10_schema/user/ak_users_constraints_fix_v1.sql
Normal file
@@ -0,0 +1,19 @@
|
||||
-- =====================================================================================
|
||||
-- Schema: public.ak_users
|
||||
-- Version: v1
|
||||
-- Purpose: 修复 auth.users -> ak_users 自动同步的写入协调问题
|
||||
-- Change: 放宽 username 和 email 的 NOT NULL 约束,以允许数据库触发器成功插入新用户记录。
|
||||
-- 同时,将 role 的默认值更新为 'customer' 以符合业务逻辑。
|
||||
-- =====================================================================================
|
||||
|
||||
BEGIN;
|
||||
|
||||
-- 步骤 1 & 2: 允许 username/email 为空,并更新 role 默认值
|
||||
-- 这样数据库的自动用户同步触发器就不会因为缺少 NOT NULL 的值而失败。
|
||||
-- 前端代码 (ensureUserProfile) 会在用户首次登录时尝试填充这些值。
|
||||
ALTER TABLE public.ak_users
|
||||
ALTER COLUMN username DROP NOT NULL,
|
||||
ALTER COLUMN email DROP NOT NULL,
|
||||
ALTER COLUMN role SET DEFAULT 'customer';
|
||||
|
||||
COMMIT;
|
||||
0
docs/sql/20_rls/.keep
Normal file
0
docs/sql/20_rls/.keep
Normal file
8
docs/sql/20_rls/README.md
Normal file
8
docs/sql/20_rls/README.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# docs/sql/20_rls
|
||||
|
||||
本目录存放:
|
||||
- RLS 启用与策略(`ENABLE ROW LEVEL SECURITY`、`CREATE/ALTER/DROP POLICY`)
|
||||
- 按域分组(如 `analytics/`、`user/` 等)
|
||||
- 文件命名:`<table>_rls_v<version>.sql`
|
||||
|
||||
**禁止**:在此目录混入 DDL、RPC、GRANT 等对象。
|
||||
0
docs/sql/30_rpc/.keep
Normal file
0
docs/sql/30_rpc/.keep
Normal file
0
docs/sql/30_rpc/README.md
Normal file
0
docs/sql/30_rpc/README.md
Normal 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;
|
||||
$$;
|
||||
19
docs/sql/30_rpc/auth/get_current_user_role_v1.sql
Normal file
19
docs/sql/30_rpc/auth/get_current_user_role_v1.sql
Normal 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;
|
||||
$$;
|
||||
76
docs/sql/30_rpc/auth/handle_new_user_v2.sql
Normal file
76
docs/sql/30_rpc/auth/handle_new_user_v2.sql
Normal 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;
|
||||
$$;
|
||||
81
docs/sql/30_rpc/auth/handle_new_user_v3.sql
Normal file
81
docs/sql/30_rpc/auth/handle_new_user_v3.sql
Normal 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;
|
||||
$$;
|
||||
0
docs/sql/40_grants/.keep
Normal file
0
docs/sql/40_grants/.keep
Normal file
0
docs/sql/40_grants/README.md
Normal file
0
docs/sql/40_grants/README.md
Normal file
0
docs/sql/90_archive/.keep
Normal file
0
docs/sql/90_archive/.keep
Normal file
7
docs/sql/90_archive/README.md
Normal file
7
docs/sql/90_archive/README.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# docs/sql/90_archive
|
||||
|
||||
本目录存放:
|
||||
- 历史/废弃 SQL(只读归档)
|
||||
- 不再作为权威引用口径
|
||||
|
||||
如需保留旧版本 RPC/策略,请迁移到此目录并在新版本中注明替代关系。
|
||||
Reference in New Issue
Block a user