feat(dc): Complete Tool C quick action buttons Phase 1-2 - 7 functions

Summary:
- Implement 7 quick action functions (filter, recode, binning, conditional, dropna, compute, pivot)
- Refactor to pre-written Python functions architecture (stable and secure)
- Add 7 Python operations modules with full type hints
- Add 7 frontend Dialog components with user-friendly UI
- Fix NaN serialization issues and auto type conversion
- Update all related documentation

Technical Details:
- Python: operations/ module (filter.py, recode.py, binning.py, conditional.py, dropna.py, compute.py, pivot.py)
- Backend: QuickActionService.ts with 7 execute methods
- Frontend: 7 Dialog components with complete validation
- Toolbar: Enable 7 quick action buttons

Status: Phase 1-2 completed, basic testing passed, ready for further testing
This commit is contained in:
2025-12-08 17:38:08 +08:00
parent af325348b8
commit f729699510
158 changed files with 13814 additions and 273 deletions

View File

@@ -17,6 +17,7 @@ import { MultipartFile } from '@fastify/multipart';
import { logger } from '../../../../common/logging/index.js';
import { sessionService } from '../services/SessionService.js';
import { dataProcessService } from '../services/DataProcessService.js';
import * as xlsx from 'xlsx';
// ==================== 请求参数类型定义 ====================
@@ -291,6 +292,76 @@ export class SessionController {
});
}
}
/**
* ✨ 导出Excel文件新增
*
* GET /api/v1/dc/tool-c/sessions/:id/export
*/
async exportData(
request: FastifyRequest<{ Params: SessionIdParams }>,
reply: FastifyReply
) {
try {
const { id } = request.params;
logger.info(`[SessionController] 导出Excel: ${id}`);
// 1. 获取Session信息
const session = await sessionService.getSession(id);
// 2. 获取完整数据
const data = await sessionService.getFullData(id);
// 3. 生成Excel
const workbook = xlsx.utils.book_new();
const worksheet = xlsx.utils.json_to_sheet(data);
// 设置列宽(自动调整)
const colWidths = session.columns.map(col => {
const maxLength = Math.max(
col.length,
...data.slice(0, 100).map(row => String(row[col] || '').length)
);
return { wch: Math.min(maxLength + 2, 50) };
});
worksheet['!cols'] = colWidths;
xlsx.utils.book_append_sheet(workbook, worksheet, 'Data');
// 4. 生成Buffer
const buffer = xlsx.write(workbook, {
type: 'buffer',
bookType: 'xlsx',
compression: true,
});
// 5. 生成文件名加上_cleaned后缀和时间戳
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
const baseFileName = session.fileName.replace(/\.[^/.]+$/, ''); // 去除扩展名
const exportFileName = `${baseFileName}_cleaned_${timestamp}.xlsx`;
logger.info(`[SessionController] 导出成功: ${exportFileName}, 大小: ${(buffer.length / 1024).toFixed(2)}KB`);
// 6. 返回文件
reply.header('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
reply.header('Content-Disposition', `attachment; filename="${encodeURIComponent(exportFileName)}"`);
reply.header('Content-Length', buffer.length);
return reply.send(buffer);
} catch (error: any) {
logger.error(`[SessionController] 导出Excel失败: ${error.message}`);
const statusCode = error.message.includes('不存在') || error.message.includes('过期')
? 404
: 500;
return reply.code(statusCode).send({
success: false,
error: error.message || '导出Excel失败',
});
}
}
}
// ==================== 导出单例实例 ====================