Files
medical-mall/mall/pages/user/test/USER_AUTH_SCHEMA.sql
2026-02-04 17:35:46 +08:00

146 lines
5.5 KiB
PL/PgSQL
Raw 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.
-- ============================================
-- 用户登录 / 注册 - 核心用户资料表结构(创建版 / Create-only
-- ============================================
-- 用途创建核心业务用户资料表ak_users及其相关函数和 RLS 策略。
-- 特点:
-- 1. 不做 DROP/DELETE/TRUNCATE不清空数据
-- 2. 通过 IF NOT EXISTS + 系统表判断,实现可重复执行
-- 3. 职责单一:只负责 ak_users不涉及其他基础表
-- 4. 依赖应在基础表01_create_tables.sql之后执行
-- ============================================
-- ============================================
-- 1. 业务用户资料表 ak_users
-- ============================================
CREATE TABLE IF NOT EXISTS public.ak_users (
id uuid primary key,
username text,
email text,
gender text,
birthday date,
height_cm numeric,
weight_kg numeric,
bio text,
avatar_url text,
preferred_language text,
role text,
school_id text,
grade_id text,
class_id text,
created_at timestamptz default now(),
updated_at timestamptz default now()
);
-- 中文注释
COMMENT ON TABLE public.ak_users IS '业务用户资料表(与 auth.users 一一对应)';
COMMENT ON COLUMN public.ak_users.id IS '用户ID等于 auth.users.id';
COMMENT ON COLUMN public.ak_users.username IS '用户名/昵称';
COMMENT ON COLUMN public.ak_users.email IS '邮箱';
COMMENT ON COLUMN public.ak_users.gender IS '性别';
COMMENT ON COLUMN public.ak_users.birthday IS '生日';
COMMENT ON COLUMN public.ak_users.height_cm IS '身高(厘米)';
COMMENT ON COLUMN public.ak_users.weight_kg IS '体重(公斤)';
COMMENT ON COLUMN public.ak_users.bio IS '个人简介';
COMMENT ON COLUMN public.ak_users.avatar_url IS '头像地址';
COMMENT ON COLUMN public.ak_users.preferred_language IS '偏好语言';
COMMENT ON COLUMN public.ak_users.role IS '角色(如 customer/merchant/admin 等)';
COMMENT ON COLUMN public.ak_users.school_id IS '学校ID可选';
COMMENT ON COLUMN public.ak_users.grade_id IS '年级ID可选';
COMMENT ON COLUMN public.ak_users.class_id IS '班级ID可选';
COMMENT ON COLUMN public.ak_users.created_at IS '创建时间';
COMMENT ON COLUMN public.ak_users.updated_at IS '更新时间';
-- 为 ak_users 添加 updated_at 触发器
-- 注意:通用函数 update_updated_at_column() 在 01_create_tables.sql 中创建
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_trigger WHERE tgname = 'update_ak_users_updated_at') THEN
EXECUTE 'CREATE TRIGGER update_ak_users_updated_at BEFORE UPDATE ON public.ak_users FOR EACH ROW EXECUTE FUNCTION public.update_updated_at_column()';
END IF;
END $$;
-- ============================================
-- 2. 行级安全策略RLS
-- ============================================
-- 启用 RLS
ALTER TABLE public.ak_users ENABLE ROW LEVEL SECURITY;
-- 仅允许本人读写自己的资料
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_policies WHERE schemaname='public' AND tablename='ak_users' AND policyname='ak_users_self_select') THEN
EXECUTE 'CREATE POLICY "ak_users_self_select" ON public.ak_users FOR SELECT USING (auth.uid() = id)';
END IF;
IF NOT EXISTS (SELECT 1 FROM pg_policies WHERE schemaname='public' AND tablename='ak_users' AND policyname='ak_users_self_insert') THEN
EXECUTE 'CREATE POLICY "ak_users_self_insert" ON public.ak_users FOR INSERT WITH CHECK (auth.uid() = id)';
END IF;
IF NOT EXISTS (SELECT 1 FROM pg_policies WHERE schemaname='public' AND tablename='ak_users' AND policyname='ak_users_self_update') THEN
EXECUTE 'CREATE POLICY "ak_users_self_update" ON public.ak_users FOR UPDATE USING (auth.uid() = id)';
END IF;
END $$;
-- ============================================
-- 3. 相关函数
-- ============================================
-- 函数1手动初始化/更新用户资料(可选,供前端调用)
CREATE OR REPLACE FUNCTION public.upsert_user_profile(
p_user_id uuid,
p_email text,
p_username text default null
)
RETURNS public.ak_users
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = public
AS $$
DECLARE
v_username text := coalesce(p_username, split_part(p_email, '@', 1), 'user');
v_result public.ak_users;
BEGIN
-- 插入或更新用户资料
INSERT INTO public.ak_users (id, email, username)
VALUES (p_user_id, p_email, v_username)
ON CONFLICT (id) DO UPDATE
SET
email = excluded.email,
username = coalesce(excluded.username, ak_users.username)
RETURNING * INTO v_result;
RETURN v_result;
END;
$$;
-- 函数2供 auth.users 触发器使用,自动创建用户资料
CREATE OR REPLACE FUNCTION public.handle_new_user()
RETURNS trigger
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = public
AS $$
BEGIN
BEGIN
INSERT INTO public.ak_users (id, email, username)
VALUES (NEW.id, NEW.email, COALESCE(SPLIT_PART(NEW.email, '@', 1), 'user'))
ON CONFLICT (id) DO NOTHING;
EXCEPTION WHEN OTHERS THEN
-- 重要:不要因为业务表写入失败而阻断 auth.users 的注册事务
RAISE WARNING 'handle_new_user failed: %', SQLERRM;
END;
RETURN NEW;
END;
$$;
-- ============================================
-- 4. 函数授权
-- ============================================
-- upsert_user_profile 只允许已登录用户调用
REVOKE ALL ON FUNCTION public.upsert_user_profile(uuid, text, text) FROM PUBLIC;
GRANT EXECUTE ON FUNCTION public.upsert_user_profile(uuid, text, text) TO authenticated;
-- handle_new_user 是触发器函数,由系统内部调用,无需对任何角色授权