refactor: comprehensive code review improvements

- Enhanced input validation and error handling across all services
- Added comprehensive JSDoc documentation for all major functions
- Improved database operations with transaction management and better error handling
- Added PropTypes validation and error boundaries for React components
- Unified error handling patterns across backend services
- Reduced code duplication in API routes with shared validation functions
- Enhanced security with stricter input validation and SQL injection prevention
- Improved user experience with better error messages and retry functionality
This commit is contained in:
patdelphi
2025-08-24 09:06:24 +08:00
parent 5378b86a9b
commit 0a62208cda
12 changed files with 541 additions and 1104 deletions

View File

@@ -80,8 +80,12 @@ class DatabaseManager {
}
}
// 迁移ai_interpretations表结构
/**
* 迁移ai_interpretations表结构
* 将旧的analysis_id字段迁移为reading_id字段建立正确的外键关系
*/
migrateAiInterpretationsTable() {
let transaction;
try {
// 检查ai_interpretations表是否存在且使用旧的analysis_id字段
const tableInfo = this.db.prepare(`
@@ -89,60 +93,100 @@ class DatabaseManager {
WHERE type='table' AND name='ai_interpretations'
`).get();
if (tableInfo) {
// 检查是否有analysis_id字段旧结构
const columnInfo = this.db.prepare(`
PRAGMA table_info(ai_interpretations)
`).all();
const hasAnalysisId = columnInfo.some(col => col.name === 'analysis_id');
const hasReadingId = columnInfo.some(col => col.name === 'reading_id');
if (hasAnalysisId && !hasReadingId) {
console.log('检测到旧的ai_interpretations表结构开始迁移...');
// 创建新表结构
this.db.exec(`
CREATE TABLE ai_interpretations_new (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
reading_id INTEGER NOT NULL,
content TEXT NOT NULL,
model TEXT,
tokens_used INTEGER,
success BOOLEAN DEFAULT 1,
error_message TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
FOREIGN KEY (reading_id) REFERENCES numerology_readings(id) ON DELETE CASCADE,
UNIQUE(reading_id)
)
`);
// 迁移数据只迁移数字ID的记录
this.db.exec(`
INSERT INTO ai_interpretations_new
(user_id, reading_id, content, model, tokens_used, success, error_message, created_at, updated_at)
SELECT user_id, CAST(analysis_id AS INTEGER), content, model, tokens_used, success, error_message, created_at, updated_at
FROM ai_interpretations
WHERE analysis_id GLOB '[0-9]*'
`);
// 删除旧表,重命名新表
this.db.exec('DROP TABLE ai_interpretations');
this.db.exec('ALTER TABLE ai_interpretations_new RENAME TO ai_interpretations');
// 重新创建索引
this.db.exec('CREATE INDEX IF NOT EXISTS idx_ai_interpretations_user_id ON ai_interpretations(user_id)');
this.db.exec('CREATE INDEX IF NOT EXISTS idx_ai_interpretations_reading_id ON ai_interpretations(reading_id)');
this.db.exec('CREATE INDEX IF NOT EXISTS idx_ai_interpretations_created_at ON ai_interpretations(created_at DESC)');
console.log('ai_interpretations表迁移完成');
}
if (!tableInfo) {
console.log('ai_interpretations表不存在跳过迁移');
return;
}
// 检查是否有analysis_id字段旧结构
const columnInfo = this.db.prepare(`
PRAGMA table_info(ai_interpretations)
`).all();
const hasAnalysisId = columnInfo.some(col => col.name === 'analysis_id');
const hasReadingId = columnInfo.some(col => col.name === 'reading_id');
if (!hasAnalysisId || hasReadingId) {
console.log('ai_interpretations表结构已是最新跳过迁移');
return;
}
console.log('检测到旧的ai_interpretations表结构开始迁移...');
// 开始事务
transaction = this.db.transaction(() => {
// 创建新表结构
this.db.exec(`
CREATE TABLE ai_interpretations_new (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
reading_id INTEGER NOT NULL,
content TEXT NOT NULL,
model TEXT,
tokens_used INTEGER,
success BOOLEAN DEFAULT 1,
error_message TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
FOREIGN KEY (reading_id) REFERENCES numerology_readings(id) ON DELETE CASCADE,
UNIQUE(reading_id)
)
`);
// 检查有多少条记录需要迁移
const countStmt = this.db.prepare(`
SELECT COUNT(*) as count FROM ai_interpretations
WHERE analysis_id GLOB '[0-9]*'
`);
const { count } = countStmt.get();
console.log(`准备迁移 ${count} 条有效记录`);
// 迁移数据只迁移数字ID的记录
const migrateStmt = this.db.prepare(`
INSERT INTO ai_interpretations_new
(user_id, reading_id, content, model, tokens_used, success, error_message, created_at, updated_at)
SELECT user_id, CAST(analysis_id AS INTEGER), content, model, tokens_used, success, error_message, created_at, updated_at
FROM ai_interpretations
WHERE analysis_id GLOB '[0-9]*'
`);
const migrateResult = migrateStmt.run();
console.log(`成功迁移 ${migrateResult.changes} 条记录`);
// 删除旧表,重命名新表
this.db.exec('DROP TABLE ai_interpretations');
this.db.exec('ALTER TABLE ai_interpretations_new RENAME TO ai_interpretations');
// 重新创建索引
this.db.exec('CREATE INDEX IF NOT EXISTS idx_ai_interpretations_user_id ON ai_interpretations(user_id)');
this.db.exec('CREATE INDEX IF NOT EXISTS idx_ai_interpretations_reading_id ON ai_interpretations(reading_id)');
this.db.exec('CREATE INDEX IF NOT EXISTS idx_ai_interpretations_created_at ON ai_interpretations(created_at DESC)');
});
// 执行事务
transaction();
console.log('ai_interpretations表迁移完成');
} catch (error) {
console.error('ai_interpretations表迁移失败:', error);
console.error('错误详情:', error.message);
// 如果事务失败尝试回滚SQLite会自动回滚失败的事务
try {
// 检查是否存在临时表,如果存在则清理
const tempTableCheck = this.db.prepare(`
SELECT name FROM sqlite_master
WHERE type='table' AND name='ai_interpretations_new'
`).get();
if (tempTableCheck) {
this.db.exec('DROP TABLE ai_interpretations_new');
console.log('已清理临时表');
}
} catch (cleanupError) {
console.error('清理临时表失败:', cleanupError);
}
// 迁移失败不应该阻止应用启动,只记录错误
}
}