fix(ssa): harden spreadsheet upload recognition and guidance
Fix SSA data-context generation for Excel uploads by parsing xlsx/xls via extension-aware paths instead of UTF-8 fallback. Add on-demand overview rebuild in Agent flow, align xls friendly prompts on frontend/backend, and surface backend upload errors to users. Made-with: Cursor
This commit is contained in:
@@ -249,18 +249,26 @@ export class DataProfileService {
|
||||
} else if (session.dataOssKey) {
|
||||
// 从 OSS 下载文件
|
||||
const buffer = await storage.download(session.dataOssKey);
|
||||
const content = buffer.toString('utf-8');
|
||||
|
||||
// 检测文件格式:JSON 或 CSV
|
||||
const trimmedContent = content.trim();
|
||||
if (trimmedContent.startsWith('[') || trimmedContent.startsWith('{')) {
|
||||
// JSON 格式
|
||||
const data = JSON.parse(content);
|
||||
return await this.generateProfile(sessionId, data);
|
||||
} else {
|
||||
// CSV 格式,直接发给 Python 解析(更高效、更可靠)
|
||||
const ext = this.getFileExtensionFromOssKey(session.dataOssKey);
|
||||
|
||||
// 按文件扩展名处理,避免将 Excel 二进制误当作 UTF-8 文本
|
||||
if (ext === 'csv') {
|
||||
const content = buffer.toString('utf-8');
|
||||
return await this.generateProfileFromCSV(sessionId, content);
|
||||
}
|
||||
|
||||
if (ext === 'xlsx' || ext === 'xls') {
|
||||
return await this.generateProfileFromExcel(sessionId, buffer);
|
||||
}
|
||||
|
||||
// 兼容历史数据:未知扩展名时再尝试 JSON/CSV 兜底
|
||||
const content = buffer.toString('utf-8');
|
||||
const trimmedContent = content.trim();
|
||||
if (trimmedContent.startsWith('[') || trimmedContent.startsWith('{')) {
|
||||
const data = JSON.parse(content);
|
||||
return await this.generateProfile(sessionId, data);
|
||||
}
|
||||
return await this.generateProfileFromCSV(sessionId, content);
|
||||
} else {
|
||||
throw new Error('No data available for session');
|
||||
}
|
||||
@@ -278,6 +286,38 @@ export class DataProfileService {
|
||||
}
|
||||
}
|
||||
|
||||
private getFileExtensionFromOssKey(ossKey: string): string {
|
||||
const match = ossKey.toLowerCase().match(/\.([a-z0-9]+)$/);
|
||||
return match?.[1] || '';
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 Excel 二进制生成画像(xlsx/xls)
|
||||
*/
|
||||
private async generateProfileFromExcel(sessionId: string, buffer: Buffer): Promise<DataProfileResult> {
|
||||
try {
|
||||
const xlsx = await import('xlsx');
|
||||
const workbook = xlsx.read(buffer, { type: 'buffer' });
|
||||
const firstSheetName = workbook.SheetNames[0];
|
||||
const firstSheet = workbook.Sheets[firstSheetName];
|
||||
const rows = xlsx.utils.sheet_to_json(firstSheet, {
|
||||
defval: null,
|
||||
raw: false,
|
||||
}) as Record<string, any>[];
|
||||
|
||||
return await this.generateProfile(sessionId, rows);
|
||||
} catch (error: any) {
|
||||
logger.error('[SSA:DataProfile] Excel profile generation failed', {
|
||||
sessionId,
|
||||
error: error.message,
|
||||
});
|
||||
return {
|
||||
success: false,
|
||||
error: error.message || 'Failed to parse Excel file',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存画像到 Session
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user