import { generateText } from "ai"; import { NextRequest, NextResponse } from "next/server"; import { z } from "zod"; import { createLogger } from "@/lib/observability/logger"; import { buildProductPlanningAnalyzePrompt } from "@/lib/product-planning/analyze"; import { resolveProductPlanningTextModel } from "@/lib/product-planning/model-runtime"; import { isProductPlanningEnabled, PRODUCT_PLANNING_DISABLED_MESSAGE, } from "@/lib/product-planning/server-feature"; import { getProductPlanningToken } from "@/lib/product-planning/server-auth"; import type { ProductPlanningAnalyzeRequest, ProductPlanningAnalyzeResponse, } from "@/lib/product-planning/types"; export const runtime = "nodejs"; export const maxDuration = 120; const logger = createLogger("product-planning-analyze-api"); const requestSchema = z.union([ z.object({ kind: z.literal("review"), rows: z .array( z.object({ itemId: z.string(), date: z.string(), content: z.string(), appendComment: z.string().optional(), sku: z.string().optional(), userId: z.string().optional(), images: z.array(z.string()), videoUrls: z.array(z.string()), }) ) .min(1), }), z.object({ kind: z.literal("qa"), rows: z .array( z.object({ itemId: z.string(), question: z.string(), questionDetail: z.string().optional(), answer: z.string(), answerUser: z.string().optional(), answerDate: z.string().optional(), isOfficial: z.boolean(), }) ) .min(1), }), ]); function jsonError(status: number, error: string) { const payload: ProductPlanningAnalyzeResponse = { success: false, error, }; return NextResponse.json(payload, { status }); } export async function POST(request: NextRequest) { const token = await getProductPlanningToken(request); if (!token?.sub) { return jsonError(401, "未登录"); } if (!(await isProductPlanningEnabled())) { return jsonError(403, PRODUCT_PLANNING_DISABLED_MESSAGE); } let body: unknown; try { body = await request.json(); } catch { return jsonError(400, "请求体不是合法 JSON"); } const parsed = requestSchema.safeParse(body); if (!parsed.success) { return jsonError(400, "请求参数不合法或 rows 为空"); } const resolvedModel = await resolveProductPlanningTextModel(); if (!resolvedModel.model) { return jsonError(500, resolvedModel.error || "当前站点没有可用的文本分析模型"); } try { const analyzeRequest = parsed.data as ProductPlanningAnalyzeRequest; const prompt = buildProductPlanningAnalyzePrompt(analyzeRequest); const { text } = await generateText({ model: resolvedModel.model, prompt, temperature: 0.3, }); const payload: ProductPlanningAnalyzeResponse = { success: true, markdown: text, }; return NextResponse.json(payload); } catch (error) { logger.error("request_failed", error); return jsonError(502, "AI 分析失败"); } }