/** * Plan generation entry point. * Tries the agent path first, falls back to legacy pipeline. */ import { prisma } from "@/lib/prisma"; import { ApiError } from "@/lib/api"; import { runAgentPlanGeneration } from "./agentPlan"; import { runLegacyPlanGeneration, PLAN_PROGRESS_MESSAGES } from "./legacyPlan"; import { enrichTransitInfo, type EnrichDay } from "./transitEnrichment"; import type { TaggedIdea } from "./ideaSelection"; export interface PlanGenAvailableTime { date: string; startHour: number; endHour: number; } export interface PlanGenResult { id: string; days: { date: string; items: unknown[]; summary: string }[]; createdAt: string; } export async function runPlanGeneration( roomId: string, userId: string, availableTime: PlanGenAvailableTime, onProgress?: (message: string) => void, ): Promise { // 1. Fetch room & ideas const room = await prisma.blindBoxRoom.findUnique({ where: { id: roomId } }); if (!room) throw new ApiError("房间不存在", 404); if (!room.lat || !room.lng) { throw new ApiError("请先设置房间位置", 400); } onProgress?.(PLAN_PROGRESS_MESSAGES.analyzing); const allIdeas = await prisma.blindBoxIdea.findMany({ where: { roomId, status: "in_pool", category: { not: null } }, select: { id: true, content: true, category: true, timeSlot: true, estimatedMinutes: true, searchQuery: true, searchType: true, costLevel: true, intensity: true, needsBooking: true, }, }); const taggedIdeas: TaggedIdea[] = allIdeas.filter( (i): i is TaggedIdea => !!i.category && !!i.timeSlot && !!i.searchQuery && !!i.searchType && typeof i.estimatedMinutes === "number", ); if (taggedIdeas.length < 2) { throw new ApiError("盒子里至少需要 2 个已标记的想法才能生成计划", 400); } // 2. Try agent path, fallback to legacy let days: { date: string; items: unknown[]; summary: string }[]; try { const agentResult = await runAgentPlanGeneration( { lat: room.lat, lng: room.lng, city: room.city ?? "" }, taggedIdeas, availableTime, onProgress, ); days = agentResult.days; } catch (e) { console.error("runAgentPlanGeneration failed, falling back to legacy:", e); onProgress?.("使用备用方案规划..."); const legacyResult = await runLegacyPlanGeneration( { lat: room.lat, lng: room.lng }, taggedIdeas, availableTime, onProgress, ); days = legacyResult.days; } // 3. Enrich transit info (best-effort) onProgress?.("正在查询交通信息..."); await enrichTransitInfo( days as EnrichDay[], room.city ?? "上海", room.lat, room.lng, ); // 4. Save to DB const plan = await prisma.weekendPlan.create({ data: { roomId, userId, planData: JSON.stringify({ days }), }, }); return { id: plan.id, days, createdAt: plan.createdAt.toISOString(), }; }