2026-05-27 13:16:37 +08:00

96 lines
2.8 KiB
TypeScript

import { generateText } from "ai";
import { NextRequest, NextResponse } from "next/server";
import { z } from "zod";
import { createLogger } from "@/lib/observability/logger";
import { buildProductPlanningSkuAnalyzePrompt } from "@/lib/product-planning/analyze";
import {
PRODUCT_PLANNING_ADVANCED_DISABLED,
PRODUCT_PLANNING_ADVANCED_DISABLED_MESSAGE,
} from "@/lib/product-planning/advanced-disabled";
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 {
ProductPlanningAnalyzeResponse,
ProductPlanningSkuAnalyzeRequest,
} from "@/lib/product-planning/types";
export const runtime = "nodejs";
export const maxDuration = 120;
const logger = createLogger("product-planning-analyze-sku-api");
const requestSchema = z.object({
itemId: z.string().trim().min(1),
title: z.string().trim().optional(),
platform: z.enum(["taobao", "jd"]),
skus: z
.array(
z.object({
name: z.string().trim().min(1),
price: z.number().nullable(),
})
)
.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) {
if (PRODUCT_PLANNING_ADVANCED_DISABLED) {
return jsonError(503, PRODUCT_PLANNING_ADVANCED_DISABLED_MESSAGE);
}
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, "请求参数不合法或 skus 为空");
}
const resolvedModel = await resolveProductPlanningTextModel();
if (!resolvedModel.model) {
return jsonError(500, resolvedModel.error || "当前站点没有可用的文本分析模型");
}
try {
const analyzeRequest = parsed.data as ProductPlanningSkuAnalyzeRequest;
const prompt = buildProductPlanningSkuAnalyzePrompt(analyzeRequest);
const { text } = await generateText({
model: resolvedModel.model,
prompt,
temperature: 0.3,
});
return NextResponse.json({
success: true,
markdown: text,
} satisfies ProductPlanningAnalyzeResponse);
} catch (error) {
logger.error("request_failed", error);
return jsonError(502, "SKU 卖点分析失败");
}
}