import { NextRequest } from 'next/server' import { z } from 'zod' import { prisma } from '@/src/lib/prisma' const ExportSchema = z.object({ ids: z.array(z.string()).min(1), format: z.enum(['csv', 'json']), }) export async function POST(req: NextRequest) { try { const body = await req.json() const parsed = ExportSchema.safeParse(body) if (!parsed.success) return Response.json({ error: 'invalid body' }, { status: 400 }) const { ids, format } = parsed.data const rows = await prisma.recording.findMany({ where: { id: { in: ids } }, orderBy: { timestamp: 'asc' }, // Cast select to any to accommodate freshly added fields before prisma generate select: { id: true, timestamp: true, code_data: true, fit_a: true, fit_b: true, sample_count: true, duration: true, rec_start_ms: true, rec_end_ms: true, } , }) if (format === 'json') { const payload = rows.map((r) => ({ id: r.id, timestamp: new Date(r.timestamp).toISOString(), code: r.code_data, fit: r.fit_a != null ? { a: r.fit_a, b: r.fit_b } : undefined, sampleCount: r.sample_count, duration: r.duration, })) return new Response(JSON.stringify(payload), { headers: { 'content-type': 'application/json; charset=utf-8', 'content-disposition': `attachment; filename="export-${Date.now()}.json"`, }, }) } // CSV(去掉 recordId、timestamp,timeSec 改为毫秒单位) let csv = 'index,timeMs,code,force\n' for (const r of rows) { // 以毫秒为单位的步长 let stepMs = r.sample_count > 0 ? (r.duration * 1000) / r.sample_count : 0 if (r.rec_start_ms != null && r.rec_end_ms != null && r.sample_count > 0) { const dtMs = Number((r.rec_end_ms as bigint) - (r.rec_start_ms as bigint)) if (Number.isFinite(dtMs) && dtMs > 0) stepMs = dtMs / r.sample_count } // 采样溢出时强制 1ms 步长(与 16.384s/16384 对齐) if (r.sample_count >= 16384) stepMs = 1 const hasFit = r.fit_a != null for (let i = 0; i < r.code_data.length; i++) { const t = (i * stepMs).toFixed(3) const code = r.code_data[i] const force = hasFit ? r.fit_a! * code + (r.fit_b ?? 0) : '' csv += `${i},${t},${code},${hasFit ? force : ''}\n` } } return new Response(csv, { headers: { 'content-type': 'text/csv; charset=utf-8', 'content-disposition': `attachment; filename="export-${Date.now()}.csv"`, }, }) } catch (err) { console.error(err) return Response.json({ error: 'internal error' }, { status: 500 }) } }