核心功能: - 新增AICodeService(550行):AI代码生成核心服务 - 新增AIController(257行):4个API端点 - 新增dc_tool_c_ai_history表:存储对话历史 - 实现自我修正机制:最多3次智能重试 - 集成LLMFactory:复用通用能力层 - 10个Few-shot示例:覆盖Level 1-4场景 技术优化: - 修复NaN序列化问题(Python端转None) - 修复数据传递问题(从Session获取真实数据) - 优化System Prompt(明确环境信息) - 调整Few-shot示例(移除import语句) 测试结果: - 通过率:9/11(81.8%) 达到MVP标准 - 成功场景:缺失值处理、编码、分箱、BMI、筛选、填补、统计、分类 - 待优化:数值清洗、智能去重(已记录技术债务TD-C-006) API端点: - POST /api/v1/dc/tool-c/ai/generate(生成代码) - POST /api/v1/dc/tool-c/ai/execute(执行代码) - POST /api/v1/dc/tool-c/ai/process(生成并执行,一步到位) - GET /api/v1/dc/tool-c/ai/history/:sessionId(对话历史) 文档更新: - 新增Day 3开发完成总结(770行) - 新增复杂场景优化技术债务(TD-C-006) - 更新工具C当前状态文档 - 更新技术债务清单 影响范围: - backend/src/modules/dc/tool-c/*(新增2个文件,更新1个文件) - backend/scripts/create-tool-c-ai-history-table.mjs(新增) - backend/prisma/schema.prisma(新增DcToolCAiHistory模型) - extraction_service/services/dc_executor.py(NaN序列化修复) - docs/03-业务模块/DC-数据清洗整理/*(5份文档更新) Breaking Changes: 无 总代码行数:+950行 Refs: #Tool-C-Day3
185 lines
5.8 KiB
JavaScript
185 lines
5.8 KiB
JavaScript
/**
|
||
* 从Cursor的SQLite数据库中恢复代码历史
|
||
*
|
||
* 使用方法:
|
||
* 1. cd backend
|
||
* 2. npm install better-sqlite3
|
||
* 3. node recover-code-from-cursor-db.js
|
||
*/
|
||
|
||
const Database = require('better-sqlite3');
|
||
const fs = require('fs');
|
||
const path = require('path');
|
||
|
||
// Cursor SQLite数据库路径
|
||
const DB_PATH = path.join(
|
||
process.env.APPDATA || process.env.HOME,
|
||
'Cursor/User/workspaceStorage/d5e3431d02cbaa0109f69d72300733da/state.vscdb'
|
||
);
|
||
|
||
// 输出目录
|
||
const OUTPUT_DIR = path.join(__dirname, 'cursor-history-recovery');
|
||
|
||
console.log('🔍 正在读取Cursor历史数据库...');
|
||
console.log('📂 数据库路径:', DB_PATH);
|
||
|
||
if (!fs.existsSync(DB_PATH)) {
|
||
console.error('❌ 数据库文件不存在!');
|
||
process.exit(1);
|
||
}
|
||
|
||
// 创建输出目录
|
||
if (!fs.existsSync(OUTPUT_DIR)) {
|
||
fs.mkdirSync(OUTPUT_DIR, { recursive: true });
|
||
}
|
||
|
||
try {
|
||
// 打开数据库(只读模式)
|
||
const db = new Database(DB_PATH, { readonly: true, fileMustExist: true });
|
||
|
||
console.log('✅ 数据库打开成功!');
|
||
|
||
// 1. 查看表结构
|
||
console.log('\n📊 数据库表列表:');
|
||
const tables = db.prepare("SELECT name FROM sqlite_master WHERE type='table'").all();
|
||
console.log(tables.map(t => ` - ${t.name}`).join('\n'));
|
||
|
||
// 2. 查询ItemTable表结构
|
||
if (tables.some(t => t.name === 'ItemTable')) {
|
||
console.log('\n📋 ItemTable 表结构:');
|
||
const columns = db.prepare("PRAGMA table_info(ItemTable)").all();
|
||
console.log(columns.map(c => ` - ${c.name} (${c.type})`).join('\n'));
|
||
|
||
// 3. 查询所有key(了解有哪些类型的数据)
|
||
console.log('\n🔑 ItemTable 中的所有key类型:');
|
||
const keys = db.prepare("SELECT DISTINCT key FROM ItemTable").all();
|
||
console.log(keys.map(k => ` - ${k.key}`).join('\n'));
|
||
|
||
// 4. 查找聊天历史相关的key
|
||
console.log('\n💬 查找聊天/Composer历史记录...');
|
||
const chatKeys = [
|
||
'workbench.panel.chat',
|
||
'composer',
|
||
'chat',
|
||
'workbench.panel.aichat',
|
||
'aiPanel'
|
||
];
|
||
|
||
let foundCount = 0;
|
||
|
||
for (const keyPattern of chatKeys) {
|
||
const rows = db.prepare(
|
||
`SELECT key, value FROM ItemTable WHERE key LIKE ?`
|
||
).all(`%${keyPattern}%`);
|
||
|
||
if (rows.length > 0) {
|
||
console.log(`\n✅ 找到 ${rows.length} 条与 "${keyPattern}" 相关的记录`);
|
||
|
||
rows.forEach((row, index) => {
|
||
foundCount++;
|
||
const filename = `${keyPattern.replace(/[^a-z0-9]/gi, '_')}_${index + 1}.json`;
|
||
const filepath = path.join(OUTPUT_DIR, filename);
|
||
|
||
// 保存原始JSON
|
||
fs.writeFileSync(filepath, row.value);
|
||
console.log(` 📄 已保存: ${filename} (${(row.value.length / 1024).toFixed(2)} KB)`);
|
||
|
||
// 尝试解析JSON并提取代码
|
||
try {
|
||
const data = JSON.parse(row.value);
|
||
|
||
// 提取可能的代码片段
|
||
const codeBlocks = extractCodeBlocks(data);
|
||
if (codeBlocks.length > 0) {
|
||
const codeFilename = `${keyPattern.replace(/[^a-z0-9]/gi, '_')}_${index + 1}_code.txt`;
|
||
const codeFilepath = path.join(OUTPUT_DIR, codeFilename);
|
||
fs.writeFileSync(codeFilepath, codeBlocks.join('\n\n' + '='.repeat(80) + '\n\n'));
|
||
console.log(` 📝 提取了 ${codeBlocks.length} 个代码块: ${codeFilename}`);
|
||
}
|
||
} catch (err) {
|
||
console.log(` ⚠️ JSON解析失败: ${err.message}`);
|
||
}
|
||
});
|
||
}
|
||
}
|
||
|
||
if (foundCount === 0) {
|
||
console.log('\n⚠️ 未找到聊天历史记录,尝试提取所有数据...');
|
||
|
||
// 导出所有ItemTable数据
|
||
const allRows = db.prepare("SELECT key, value FROM ItemTable").all();
|
||
console.log(`\n📦 共有 ${allRows.length} 条记录,正在导出...`);
|
||
|
||
const allDataFile = path.join(OUTPUT_DIR, 'all_itemtable_data.json');
|
||
fs.writeFileSync(allDataFile, JSON.stringify(allRows, null, 2));
|
||
console.log(`✅ 已导出所有数据到: all_itemtable_data.json (${(fs.statSync(allDataFile).size / 1024 / 1024).toFixed(2)} MB)`);
|
||
}
|
||
|
||
} else {
|
||
console.log('\n❌ ItemTable 表不存在!');
|
||
}
|
||
|
||
db.close();
|
||
console.log(`\n✅ 恢复完成!所有文件保存在: ${OUTPUT_DIR}`);
|
||
console.log('\n💡 下一步:');
|
||
console.log(' 1. 检查 cursor-history-recovery 文件夹');
|
||
console.log(' 2. 打开 .json 文件查找DC模块相关的代码');
|
||
console.log(' 3. 查找关键词:DualModelExtractionService, HealthCheckService, ExtractionController');
|
||
|
||
} catch (error) {
|
||
console.error('❌ 错误:', error.message);
|
||
console.error(error.stack);
|
||
process.exit(1);
|
||
}
|
||
|
||
/**
|
||
* 从JSON数据中递归提取代码块
|
||
*/
|
||
function extractCodeBlocks(obj, blocks = []) {
|
||
if (typeof obj === 'string') {
|
||
// 查找代码块模式
|
||
const codePatterns = [
|
||
/```[\s\S]*?```/g, // Markdown代码块
|
||
/export\s+(const|function|class)\s+\w+/g, // TypeScript导出
|
||
/interface\s+\w+/g, // TypeScript接口
|
||
/async\s+function\s+\w+/g, // 异步函数
|
||
];
|
||
|
||
codePatterns.forEach(pattern => {
|
||
const matches = obj.match(pattern);
|
||
if (matches) {
|
||
blocks.push(...matches);
|
||
}
|
||
});
|
||
|
||
// 如果包含关键代码关键词,保存整段
|
||
const keywords = [
|
||
'DualModelExtractionService',
|
||
'HealthCheckService',
|
||
'TemplateService',
|
||
'ConflictDetectionService',
|
||
'ExtractionController',
|
||
'dc_extraction_tasks',
|
||
'dc_health_checks'
|
||
];
|
||
|
||
if (keywords.some(kw => obj.includes(kw))) {
|
||
blocks.push(obj);
|
||
}
|
||
} else if (Array.isArray(obj)) {
|
||
obj.forEach(item => extractCodeBlocks(item, blocks));
|
||
} else if (obj && typeof obj === 'object') {
|
||
Object.values(obj).forEach(value => extractCodeBlocks(value, blocks));
|
||
}
|
||
|
||
return blocks;
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|