-- ============================================ -- 用户登录 / 注册 - 核心用户资料表结构(创建版 / 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 是触发器函数,由系统内部调用,无需对任何角色授权