import { NextRequest, NextResponse } from "next/server"; import fs from "node:fs"; import path from "node:path"; import { parseDocxToRecipe, fillTemplate, extractTemplateRecipe, type Recipe } from "@/lib/editor135-core"; export const runtime = "nodejs"; function loadTemplate(): string { // 优先取项目根目录可替换的外部模板, 其次内置模板 const external = path.join(process.cwd(), "template135.html"); if (fs.existsSync(external)) return fs.readFileSync(external, "utf8"); return fs.readFileSync(path.join(process.cwd(), "public", "template135.html"), "utf8"); } export async function POST(req: NextRequest) { try { const form = await req.formData(); const file = form.get("file"); if (!(file instanceof File)) { return NextResponse.json({ error: "请上传文件(表单字段名 file)" }, { status: 400 }); } if (!/\.docx$/i.test(file.name)) { return NextResponse.json({ error: "仅支持 .docx 文件" }, { status: 400 }); } const buf = Buffer.from(await file.arrayBuffer()); const recipe: Recipe = await parseDocxToRecipe(buf); if (!recipe.days.length) { return NextResponse.json( { error: "未能从文档中识别出食谱(需要包含 星期一~星期五 与 早餐/早点/午餐/体弱儿餐/午点 等关键词)" }, { status: 422 }, ); } const template = loadTemplate(); const templateRecipe = extractTemplateRecipe(template); const { html, report } = fillTemplate(template, recipe); const totalItems = recipe.days.reduce((n, d) => n + d.meals.reduce((k, m) => k + m.items.length, 0), 0); return NextResponse.json({ fileName: file.name, recipe, report, html, summary: { days: recipe.days.length, totalItems, templateDays: templateRecipe.days.length, templateItems: templateRecipe.days.reduce((n, d) => n + d.meals.reduce((k, m) => k + m.items.length, 0), 0), }, }); } catch (e) { const msg = e instanceof Error ? e.message : String(e); return NextResponse.json({ error: `解析失败: ${msg}` }, { status: 500 }); } }