Files
AIclinicalresearch/backend/recover-code-from-cursor-db.js
HaHafeng 91cab452d1 fix(dc/tool-c): Fix special character handling and improve UX
Major fixes:
- Fix pivot transformation with special characters in column names
- Fix compute column validation for Chinese punctuation
- Fix recode dialog to fetch unique values from full dataset via new API
- Add column mapping mechanism to handle special characters

Database migration:
- Add column_mapping field to dc_tool_c_sessions table
- Migration file: 20251208_add_column_mapping

UX improvements:
- Darken table grid lines for better visibility
- Reduce column width by 40% with tooltip support
- Insert new columns next to source columns
- Preserve original row order after operations
- Add notice about 50-row preview limit

Modified files:
- Backend: SessionService, SessionController, QuickActionService, routes
- Python: pivot.py, compute.py, recode.py, binning.py, conditional.py
- Frontend: DataGrid, RecodeDialog, index.tsx, ag-grid-custom.css
- Database: schema.prisma, migration SQL

Status: Code complete, database migrated, ready for testing
2025-12-08 23:20:55 +08:00

188 lines
5.8 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 从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;
}