diff --git a/furniture-3d-system/src/app/api/supply-chain/product-sabc/departments/export/route.ts b/furniture-3d-system/src/app/api/supply-chain/product-sabc/departments/export/route.ts index 9c4dcc00..3d99f135 100644 --- a/furniture-3d-system/src/app/api/supply-chain/product-sabc/departments/export/route.ts +++ b/furniture-3d-system/src/app/api/supply-chain/product-sabc/departments/export/route.ts @@ -60,6 +60,7 @@ function applyMetricFormats(sheet: XLSX.WorkSheet, rowCount: number, startColumn export async function GET(request: NextRequest) { return handleRequest(request, async (ctx) => { + const startedAt = Date.now() const access = await supplyChainPermissionSettingsService.getUserAccess(ctx.user, 'businessManagement') if (!access.view) { return jsonError(ErrorCode.AUTH_PERMISSION_DENIED, '当前账号没有商品 SABC 查看权限', 403, ctx.requestId) @@ -139,7 +140,12 @@ export async function GET(request: NextRequest) { XLSX.utils.book_append_sheet(workbook, sheet, sheetName) } - const content = XLSX.write(workbook, { type: 'buffer', bookType: 'xlsx' }) as Buffer + const content = XLSX.write(workbook, { type: 'buffer', bookType: 'xlsx', compression: true }) as Buffer + console.info('[department-distribution] 导出完成', { + block, + bytes: content.length, + ms: Date.now() - startedAt, + }) const stamp = new Date().toISOString().slice(0, 19).replace(/[-:T]/g, '') const blockLabel = block === 'operations' ? '-运营中心' : block === 'designers' ? '-产品中心' : block === 'purchasers' ? '-采购中心' : '' let exportedRows = 0 diff --git a/furniture-3d-system/src/app/api/supply-chain/product-sabc/export/route.ts b/furniture-3d-system/src/app/api/supply-chain/product-sabc/export/route.ts index f7cd5d6c..c55fc7c4 100644 --- a/furniture-3d-system/src/app/api/supply-chain/product-sabc/export/route.ts +++ b/furniture-3d-system/src/app/api/supply-chain/product-sabc/export/route.ts @@ -166,6 +166,7 @@ async function appendOverviewSheets(input: { export async function GET(request: NextRequest) { return handleRequest(request, async (ctx) => { + const startedAt = Date.now() const access = await supplyChainPermissionSettingsService.getUserAccess(ctx.user, 'businessManagement') if (!access.download) { return jsonError(ErrorCode.AUTH_PERMISSION_DENIED, '当前账号没有导出权限', 403, ctx.requestId) @@ -202,7 +203,14 @@ export async function GET(request: NextRequest) { if (parsed.data.mode !== 'detail') { await appendOverviewSheets({ workbook, rows, year: parsed.data.year, month: parsed.data.month }) } - const content = XLSX.write(workbook, { type: 'buffer', bookType: 'xlsx' }) as Buffer + // compression 打开后文件体积约为原来的 1/4(8 月全量 66MB → 17MB),正式环境下载更稳 + const content = XLSX.write(workbook, { type: 'buffer', bookType: 'xlsx', compression: true }) as Buffer + console.info('[product-sabc] 导出完成', { + mode: parsed.data.mode, + rows: rows.length, + bytes: content.length, + ms: Date.now() - startedAt, + }) return new NextResponse(new Uint8Array(content), { status: 200, diff --git a/furniture-3d-system/src/app/supply-chain/management/blank-pages/DepartmentDistributionView.tsx b/furniture-3d-system/src/app/supply-chain/management/blank-pages/DepartmentDistributionView.tsx index 6cd41437..b6086048 100644 --- a/furniture-3d-system/src/app/supply-chain/management/blank-pages/DepartmentDistributionView.tsx +++ b/furniture-3d-system/src/app/supply-chain/management/blank-pages/DepartmentDistributionView.tsx @@ -212,7 +212,7 @@ export function DepartmentDistributionView({ const response = await fetch(`/api/supply-chain/product-sabc/departments/export?${params}`, { cache: 'no-store' }) if (!response.ok) { const payload = await response.json().catch(() => null) - throw new Error(payload?.error?.message || '导出失败') + throw new Error(payload?.error?.message || `导出失败(HTTP ${response.status})`) } const blob = await response.blob() const disposition = response.headers.get('Content-Disposition') ?? '' @@ -227,7 +227,7 @@ export function DepartmentDistributionView({ link.remove() window.URL.revokeObjectURL(url) } catch (cause) { - setError(cause instanceof Error ? cause.message : '导出失败') + setError(cause instanceof Error ? `导出失败:${cause.message}` : '导出失败') } finally { setExporting(false) } diff --git a/furniture-3d-system/src/app/supply-chain/management/blank-pages/ProductSabcView.tsx b/furniture-3d-system/src/app/supply-chain/management/blank-pages/ProductSabcView.tsx index 71259bcf..3f758c80 100644 --- a/furniture-3d-system/src/app/supply-chain/management/blank-pages/ProductSabcView.tsx +++ b/furniture-3d-system/src/app/supply-chain/management/blank-pages/ProductSabcView.tsx @@ -243,7 +243,7 @@ export function ProductSabcView({ definition, canCalculate, canDownload }: { def const response = await fetch(`/api/supply-chain/product-sabc/export?${params}`, { cache: 'no-store' }) if (!response.ok) { const payload = await response.json().catch(() => null) - throw new Error(payload?.error?.message || '导出失败') + throw new Error(payload?.error?.message || `导出失败(HTTP ${response.status})`) } const blob = await response.blob() const disposition = response.headers.get('Content-Disposition') || '' @@ -260,7 +260,8 @@ export function ProductSabcView({ definition, canCalculate, canDownload }: { def const modeLabel = mode === 'detail' ? '明细' : mode === 'detailOverview' ? '明细(含经营总览)' : '经营总览' setMessage(`导出完成(${modeLabel}):${response.headers.get('X-Exported-Row-Count') || '全部'} 条`) } catch (cause) { - setError(cause instanceof Error ? cause.message : '导出失败') + // 常见失败:文件太大被网关截断/超时、进程内存不足(表现为网络错误) + setError(cause instanceof Error ? `导出失败:${cause.message}` : '导出失败') } finally { setExporting(false) }