feat: Excel 导入预检与动态问答,AI 聊天/文件解析体验修复
- imports: 新增 preflight_import 预检报告(判定/分阶段统计/阻断归因/问题/下一步/错误示例),导入任务 settings 落库(映射/校区/更新/重复/未匹配策略),预览应用策略,向导提交写操作日志 - ai-chat: 新增 excel_analyze(ExcelJS)工具,移除附件/上下文截断,start_import_wizard 支持确认参数,ui.import_preflight SSE,预览确认写操作日志 - admin: ImportPreflightCard 渲染与持久化,聊天抽屉布局/侧边栏修复,考勤页 CSS 引入,费用/学生页接口 schema 校验修复
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
import { BadRequestException } from '@nestjs/common';
|
||||
import * as ExcelJS from 'exceljs';
|
||||
import { Readable } from 'node:stream';
|
||||
import { cellValue, textValue } from './imports.helpers';
|
||||
import type { CellValue } from './imports.types';
|
||||
|
||||
@@ -37,3 +39,50 @@ export function extractSheets(workbook: ExcelJS.Workbook): ImportSheetData[] {
|
||||
}
|
||||
return sheets;
|
||||
}
|
||||
|
||||
export type WorkbookKind = 'csv' | 'xlsx';
|
||||
|
||||
export function detectWorkbookKind(originalName: string, mimeType: string): WorkbookKind | null {
|
||||
const isCsv =
|
||||
/\.csv$/i.test(originalName) ||
|
||||
/csv/i.test(mimeType) ||
|
||||
/text\/(csv|plain)/i.test(mimeType);
|
||||
const isXlsx =
|
||||
/\.xlsx$/i.test(originalName) ||
|
||||
/spreadsheetml/i.test(mimeType) ||
|
||||
/excel/i.test(mimeType);
|
||||
if (isCsv) return 'csv';
|
||||
if (isXlsx) return 'xlsx';
|
||||
return null;
|
||||
}
|
||||
|
||||
/** 校验文件类型并解析为工作表数据;解析失败抛出可读错误。 */
|
||||
export async function parseSheets(
|
||||
buffer: Buffer,
|
||||
originalName: string,
|
||||
mimeType: string,
|
||||
): Promise<ImportSheetData[]> {
|
||||
const kind = detectWorkbookKind(originalName, mimeType);
|
||||
if (!kind) {
|
||||
throw new BadRequestException('仅支持 .xlsx / .csv 文件');
|
||||
}
|
||||
if (/\.xls$/i.test(originalName) && !/\.xlsx$/i.test(originalName)) {
|
||||
throw new BadRequestException('暂不支持 .xls,请另存为 .xlsx 或 .csv 后重试');
|
||||
}
|
||||
try {
|
||||
const workbook = new ExcelJS.Workbook();
|
||||
if (kind === 'csv') {
|
||||
await workbook.csv.read(Readable.from(Buffer.from(buffer)));
|
||||
} else {
|
||||
await workbook.xlsx.load(buffer.buffer as ArrayBuffer);
|
||||
}
|
||||
const sheets = extractSheets(workbook);
|
||||
if (sheets.length === 0) {
|
||||
throw new BadRequestException('文件中没有可用的工作表数据');
|
||||
}
|
||||
return sheets;
|
||||
} catch (error) {
|
||||
if (error instanceof BadRequestException) throw error;
|
||||
throw new BadRequestException('Excel 文件解析失败,请检查文件格式');
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user