-- ===================================================================================== -- 4. 创建聊天会话表 (ml_chat_rooms) -- 用于优化聊天列表查询性能,维护用户与商家的会话状态 -- 依赖表: public.ak_users, public.ml_chat_messages -- ===================================================================================== -- 1. 创建表结构 CREATE TABLE IF NOT EXISTS public.ml_chat_rooms ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), user_id UUID NOT NULL REFERENCES public.ak_users(id) ON DELETE CASCADE, -- 消费者ID merchant_id UUID NOT NULL, -- 商家ID/店铺ID shop_name VARCHAR(100), -- 缓存店铺名称 shop_logo TEXT, -- 缓存店铺Logo last_message TEXT, -- 最后一条消息内容预览 last_message_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), -- 最后一条消息时间 unread_count INTEGER DEFAULT 0, -- 未读消息数 is_top BOOLEAN DEFAULT FALSE, -- 是否置顶 created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), -- 确保同一个用户和同一个商家只有一个会话 UNIQUE(user_id, merchant_id) ); COMMENT ON TABLE public.ml_chat_rooms IS '聊天会话列表表'; -- 2. 开启 RLS ALTER TABLE public.ml_chat_rooms ENABLE ROW LEVEL SECURITY; -- 3. RLS 策略 -- 策略:用户只能查自己的会话 CREATE POLICY ml_chat_rooms_select_policy ON public.ml_chat_rooms FOR SELECT USING ( auth.uid() = (SELECT auth_id FROM public.ak_users WHERE id = user_id) ); -- 策略:允许插入(通常由触发器或后端逻辑维护) CREATE POLICY ml_chat_rooms_insert_policy ON public.ml_chat_rooms FOR INSERT WITH CHECK ( auth.uid() = (SELECT auth_id FROM public.ak_users WHERE id = user_id) ); -- 策略:允许更新(如清除未读数) CREATE POLICY ml_chat_rooms_update_policy ON public.ml_chat_rooms FOR UPDATE USING ( auth.uid() = (SELECT auth_id FROM public.ak_users WHERE id = user_id) ); -- ===================================================================================== -- 4. 触发器函数:自动维护会话列表 -- ===================================================================================== CREATE OR REPLACE FUNCTION public.handle_new_chat_message() RETURNS TRIGGER AS $$ DECLARE v_user_id UUID; v_merchant_id UUID; v_shop_name TEXT; v_shop_logo TEXT; v_is_from_user BOOLEAN; v_count_inc INTEGER; BEGIN -- 确定谁是用户,谁是商家 -- 假设 is_from_user 为 true 时,sender_id 是用户,receiver_id 是商家 -- 假设 is_from_user 为 false 时,sender_id 是商家,receiver_id 是用户 v_is_from_user := NEW.is_from_user; IF v_is_from_user THEN v_user_id := NEW.sender_id; v_merchant_id := NEW.receiver_id; v_count_inc := 0; -- 用户自己发的,未读数不变 ELSE v_user_id := NEW.receiver_id; v_merchant_id := NEW.sender_id; v_count_inc := 1; -- 商家发的,用户未读数 +1 END IF; -- 尝试更新用户的会话 UPDATE public.ml_chat_rooms SET last_message = NEW.content, last_message_at = NEW.created_at, updated_at = NOW(), unread_count = unread_count + v_count_inc WHERE user_id = v_user_id AND merchant_id = v_merchant_id; -- 如果没有更新到任何行(说明会话不存在),则插入新会话 IF NOT FOUND THEN -- 尝试从 ml_shops 表获取店铺信息(如果有的话) BEGIN SELECT shop_name, shop_logo INTO v_shop_name, v_shop_logo FROM public.ml_shops WHERE merchant_id = v_merchant_id LIMIT 1; EXCEPTION WHEN OTHERS THEN -- 忽略错误,使用默认值 v_shop_name := '未知店铺'; v_shop_logo := ''; END; -- 如果没查到,给个默认名 IF v_shop_name IS NULL THEN v_shop_name := '店铺 ' || substr(v_merchant_id::text, 1, 8); END IF; INSERT INTO public.ml_chat_rooms ( user_id, merchant_id, last_message, last_message_at, unread_count, shop_name, shop_logo ) VALUES ( v_user_id, v_merchant_id, NEW.content, NEW.created_at, v_count_inc, v_shop_name, v_shop_logo ); END IF; RETURN NEW; END; $$ LANGUAGE plpgsql SECURITY DEFINER; -- 5. 创建触发器 DROP TRIGGER IF EXISTS on_chat_message_inserted ON public.ml_chat_messages; CREATE TRIGGER on_chat_message_inserted AFTER INSERT ON public.ml_chat_messages FOR EACH ROW EXECUTE FUNCTION public.handle_new_chat_message();