如何在Postgres数据满足特定条件时触发后台生成Report任务?
用户购完区域所有报告后自动触发后台生成新报告的实现方案
一、先修正表结构语法问题
你提供的SQL存在语法错误,先修正为可执行的生产版本:
-- 定义region枚举类型 create type region as enum ( 'America', 'Europe', 'Asia', 'Africa' ); -- 创建report表 create table report ( id serial primary key, region region, generated_text text -- 修正冒号语法错误 ); -- 创建report_user关联表(添加外键约束与唯一索引) create table report_user( id serial primary key, report_id int not null references report(id), -- 关联report主键(serial对应int类型) user_id uuid not null references auth.users(id), -- 关联Supabase内置用户表 unique(report_id, user_id) -- 防止用户重复购买同一报告 );
二、创建数据库触发器函数(检查购完状态)
编写PostgreSQL函数,在用户插入购买记录后,自动检查该用户是否已购买对应区域的所有报告:
create or replace function check_if_all_reports_purchased() returns trigger as $$ declare target_region region; total_region_reports int; user_purchased_count int; begin -- 获取当前购买报告所属的区域 select region into target_region from report where id = new.report_id; -- 统计该区域的总报告数量 select count(*) into total_region_reports from report where region = target_region; -- 统计当前用户在该区域已购买的报告数 select count(distinct ru.report_id) into user_purchased_count from report_user ru join report r on ru.report_id = r.id where ru.user_id = new.user_id and r.region = target_region; -- 若用户已购买该区域所有报告,触发后台生成任务 if user_purchased_count = total_region_reports then -- 调用Supabase Edge Function异步执行AI生成逻辑 perform http_post( 'https://<你的Supabase项目ID>.functions.supabase.co/generate-new-report', json_build_object('region', target_region)::text, 'application/json' ); end if; return new; end; $$ language plpgsql security definer;
三、创建触发器绑定购买事件
将触发器函数关联到report_user表的INSERT操作上,确保每次购买后自动执行检查:
create trigger after_report_purchase after insert on report_user for each row execute function check_if_all_reports_purchased();
四、编写Supabase Edge Function处理AI生成逻辑
创建异步Edge Function处理AI调用与报告插入,避免阻塞数据库事务:
// supabase/functions/generate-new-report/index.ts import { serve } from "https://deno.land/std@0.168.0/http/server.ts"; import { createClient } from "https://esm.sh/@supabase/supabase-js@2"; serve(async (req) => { const { region } = await req.json(); const supabase = createClient( Deno.env.get("SUPABASE_URL")!, Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")! ); try { // 调用AI API生成报告内容(替换为你的AI接口逻辑) const aiResponse = await fetch("https://你的AI服务接口地址", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ prompt: `生成一份关于${region}的行业分析报告` }) }); const generatedText = await aiResponse.text(); // 将新报告插入数据库 await supabase.from("report").insert({ region, generated_text: generatedText }); return new Response(JSON.stringify({ success: true }), { status: 200 }); } catch (error) { console.error("报告生成失败:", error); return new Response(JSON.stringify({ success: false, error: error.message }), { status: 500 }); } });
部署该Edge Function到Supabase:
supabase functions deploy generate-new-report
五、NextJS购买流程调整
在NextJS的购买接口中,保持原有逻辑优先返回未购买报告,无可用报告时直接告知用户后台生成中:
// app/api/purchase/route.ts import { createClient } from '@supabase/supabase-js'; import { NextResponse } from 'next/server'; const supabase = createClient(process.env.NEXT_PUBLIC_SUPABASE_URL!, process.env.SUPABASE_SERVICE_ROLE_KEY!); export async function POST(req: Request) { const { userId, region } = await req.json(); // 查询用户未购买的该区域报告 const { data: availableReports } = await supabase .from('report') .select('id') .not('id', 'in', () => supabase.from('report_user') .select('report_id') .eq('user_id', userId) ) .eq('region', region) .limit(1); if (availableReports?.length) { // 插入购买记录并返回报告ID await supabase.from('report_user').insert({ report_id: availableReports[0].id, user_id: userId }); return NextResponse.json({ reportId: availableReports[0].id }); } else { // 无可用报告,告知用户后台生成中(触发器会自动触发生成逻辑) return NextResponse.json({ message: '报告正在生成,请稍后刷新查看' }, { status: 202 }); } }
关键注意事项
- 异步优先:必须用Edge Function或异步任务队列处理AI生成,禁止在数据库触发器中直接执行慢操作,避免阻塞事务。
- 权限安全:触发器函数使用
security definer,需严格控制函数权限,避免SQL注入风险。 - 幂等处理:AI生成逻辑需添加幂等校验,比如生成前检查该区域是否已有待生成的报告,避免重复生成。
内容的提问来源于stack exchange,提问作者Evanss
相关产品推荐
相关产品推荐

