import * as fs from 'fs'; import * as path from 'path'; import ExcelJS from 'exceljs'; import PDFDocument from 'pdfkit'; import { FINANCE_BANK_ACCOUNT_TYPE_LABELS, type FinanceBankAccountDto, } from '@dukang/shared-types'; import { pickExportColumns, type ExportColumnDef } from '../../common/export/column-export.util'; function resolvePdfFontPath(): string { const candidates = [ process.env.EXPORT_PDF_FONT_PATH, path.join(process.cwd(), 'assets', 'fonts', 'NotoSansSC-Regular.otf'), path.join(process.cwd(), 'assets', 'fonts', 'simhei.ttf'), path.join(process.cwd(), 'assets', 'fonts', 'msyh.ttc'), path.join(process.cwd(), 'dist', 'assets', 'fonts', 'simhei.ttf'), '/usr/share/fonts/truetype/noto/NotoSansCJK-Regular.ttc', '/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc', 'C:\\Windows\\Fonts\\simhei.ttf', 'C:\\Windows\\Fonts\\msyh.ttc', ].filter(Boolean) as string[]; for (const candidate of candidates) { if (fs.existsSync(candidate)) return candidate; } throw new Error('未找到可用于 PDF 的中文字体,请将字体文件放到 server/dukang-api/assets/fonts/'); } function columnDefs(): ExportColumnDef[] { return [ { key: 'type', header: '类型', value: (r) => FINANCE_BANK_ACCOUNT_TYPE_LABELS[r.type] }, { key: 'ownerName', header: '归属', value: (r) => r.ownerName }, { key: 'cityName', header: '城市', value: (r) => r.cityName ?? '' }, { key: 'bankAccountName', header: '户名', value: (r) => r.bankAccountName }, { key: 'bankAccountNo', header: '银行账号', value: (r) => r.bankAccountNo }, { key: 'bankBranch', header: '开户行', value: (r) => r.bankBranch ?? '' }, { key: 'isDefault', header: '默认', value: (r) => (r.type === 'STORE' ? (r.isDefault ? '是' : '否') : '') }, { key: 'remark', header: '备注', value: (r) => r.remark ?? '' }, ]; } export async function buildFinanceBankAccountsXlsx(rows: FinanceBankAccountDto[]): Promise { const cols = pickExportColumns(columnDefs()); const workbook = new ExcelJS.Workbook(); const sheet = workbook.addWorksheet('银行账户'); sheet.addRow(cols.map((c) => c.header)); for (const row of rows) { sheet.addRow(cols.map((c) => c.value(row))); } sheet.columns.forEach((col) => { col.width = 18; }); const buffer = await workbook.xlsx.writeBuffer(); return Buffer.from(buffer); } export async function buildFinanceBankAccountsPdf(rows: FinanceBankAccountDto[]): Promise { const cols = pickExportColumns(columnDefs()); const fontPath = resolvePdfFontPath(); const chunks: Buffer[] = []; return new Promise((resolve, reject) => { const doc = new PDFDocument({ size: 'A4', layout: 'landscape', margin: 24, bufferPages: true }); doc.on('data', (c) => chunks.push(c as Buffer)); doc.on('end', () => resolve(Buffer.concat(chunks))); doc.on('error', reject); doc.font(fontPath); doc.fontSize(12).text('银行账户'); doc.moveDown(0.4); doc.fontSize(9).text(cols.map((c) => c.header).join(' | ')); doc.moveDown(0.3); for (const row of rows) { doc.text(cols.map((c) => String(c.value(row))).join(' | ')); } doc.end(); }); } export function buildFinanceBankExportFilename(format: 'xlsx' | 'pdf', count: number): string { const stamp = new Date().toISOString().slice(0, 10); return `银行账户_${stamp}_${count}.${format}`; }