|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import { fileURLToPath } from 'url'; |
|
|
import path from 'path'; |
|
|
import { |
|
|
getCelebrityCases, |
|
|
getCelebrityCaseById, |
|
|
getDb, |
|
|
} from '../server/database.js'; |
|
|
import { generateCelebrityAnalysis } from '../server/celebrityAnalyzer.js'; |
|
|
|
|
|
const __filename = fileURLToPath(import.meta.url); |
|
|
const __dirname = path.dirname(__filename); |
|
|
|
|
|
|
|
|
const args = process.argv.slice(2); |
|
|
const FORCE_REGENERATE = args.includes('--force'); |
|
|
const CONCURRENCY = parseInt(args.find(a => a.startsWith('--concurrency='))?.split('=')[1] || '2'); |
|
|
const SPECIFIC_ID = args.find(a => a.startsWith('--id='))?.split('=')[1]; |
|
|
|
|
|
|
|
|
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms)); |
|
|
|
|
|
|
|
|
let completed = 0; |
|
|
let failed = 0; |
|
|
let skipped = 0; |
|
|
let total = 0; |
|
|
|
|
|
function printProgress() { |
|
|
const progress = Math.round((completed + failed + skipped) / total * 100); |
|
|
console.log(`\n[进度] ${progress}% | 完成: ${completed} | 失败: ${failed} | 跳过: ${skipped} | 总计: ${total}`); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function saveAnalysisToDb(id, analysisResult) { |
|
|
const db = getDb(); |
|
|
const updateStmt = db.prepare(` |
|
|
UPDATE celebrity_cases |
|
|
SET analysis_data = ?, |
|
|
scores = ?, |
|
|
financial_data = ?, |
|
|
honors = ?, |
|
|
analysis_generated_at = ?, |
|
|
analysis_version = COALESCE(analysis_version, 0) + 1 |
|
|
WHERE id = ? |
|
|
`); |
|
|
|
|
|
updateStmt.run( |
|
|
JSON.stringify(analysisResult.analysisData), |
|
|
JSON.stringify(analysisResult.scores), |
|
|
JSON.stringify(analysisResult.financialData || null), |
|
|
JSON.stringify(analysisResult.honors || []), |
|
|
analysisResult.generatedAt, |
|
|
id |
|
|
); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async function processCelebrityCase(celebrity, index) { |
|
|
const { id, name_cn, name, category, analysis_data } = celebrity; |
|
|
const displayName = name_cn || name; |
|
|
|
|
|
console.log(`\n[${index + 1}/${total}] 处理: ${displayName} (${id})`); |
|
|
console.log(` 类别: ${celebrity.category_cn || category}`); |
|
|
|
|
|
|
|
|
if (analysis_data && !FORCE_REGENERATE) { |
|
|
console.log(` ✓ 已有分析数据,跳过`); |
|
|
skipped++; |
|
|
return { success: true, skipped: true }; |
|
|
} |
|
|
|
|
|
if (FORCE_REGENERATE && analysis_data) { |
|
|
console.log(` ⟳ 强制重新生成...`); |
|
|
} else { |
|
|
console.log(` ⚡ 开始生成分析...`); |
|
|
} |
|
|
|
|
|
const startTime = Date.now(); |
|
|
|
|
|
try { |
|
|
|
|
|
const fullCase = getCelebrityCaseById(id); |
|
|
if (!fullCase) { |
|
|
console.log(` ✗ 案例不存在`); |
|
|
failed++; |
|
|
return { success: false, error: '案例不存在' }; |
|
|
} |
|
|
|
|
|
|
|
|
const analysisResult = await generateCelebrityAnalysis(fullCase); |
|
|
|
|
|
if (!analysisResult.success) { |
|
|
console.log(` ✗ 生成失败: ${analysisResult.error}`); |
|
|
failed++; |
|
|
return { success: false, error: analysisResult.error }; |
|
|
} |
|
|
|
|
|
|
|
|
saveAnalysisToDb(id, analysisResult); |
|
|
|
|
|
const elapsed = ((Date.now() - startTime) / 1000).toFixed(1); |
|
|
console.log(` ✓ 生成成功 (${elapsed}s, 模型: ${analysisResult.model})`); |
|
|
|
|
|
|
|
|
if (analysisResult.analysisData?.summary) { |
|
|
const summary = analysisResult.analysisData.summary.substring(0, 100); |
|
|
console.log(` 📝 摘要: ${summary}...`); |
|
|
} |
|
|
|
|
|
if (analysisResult.scores) { |
|
|
console.log(` 📊 评分: 综合${analysisResult.scores.overall} | 性格${analysisResult.scores.personality} | 事业${analysisResult.scores.career}`); |
|
|
} |
|
|
|
|
|
completed++; |
|
|
return { success: true, elapsed, model: analysisResult.model }; |
|
|
|
|
|
} catch (error) { |
|
|
console.log(` ✗ 异常: ${error.message}`); |
|
|
failed++; |
|
|
return { success: false, error: error.message }; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async function processWithConcurrency(cases, concurrency) { |
|
|
const results = []; |
|
|
|
|
|
for (let i = 0; i < cases.length; i += concurrency) { |
|
|
const batch = cases.slice(i, i + concurrency); |
|
|
const batchPromises = batch.map((c, idx) => processCelebrityCase(c, i + idx)); |
|
|
|
|
|
const batchResults = await Promise.all(batchPromises); |
|
|
results.push(...batchResults); |
|
|
|
|
|
|
|
|
printProgress(); |
|
|
|
|
|
|
|
|
if (i + concurrency < cases.length) { |
|
|
console.log(`\n[等待] 批次间隔 3 秒...`); |
|
|
await delay(3000); |
|
|
} |
|
|
} |
|
|
|
|
|
return results; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async function main() { |
|
|
console.log('═'.repeat(60)); |
|
|
console.log(' 名人案例分析批量生成脚本'); |
|
|
console.log('═'.repeat(60)); |
|
|
console.log(`配置:`); |
|
|
console.log(` - 强制重新生成: ${FORCE_REGENERATE ? '是' : '否'}`); |
|
|
console.log(` - 并发数量: ${CONCURRENCY}`); |
|
|
if (SPECIFIC_ID) { |
|
|
console.log(` - 指定ID: ${SPECIFIC_ID}`); |
|
|
} |
|
|
console.log(''); |
|
|
|
|
|
|
|
|
let cases; |
|
|
if (SPECIFIC_ID) { |
|
|
const specificCase = getCelebrityCaseById(SPECIFIC_ID); |
|
|
if (!specificCase) { |
|
|
console.error(`错误: 找不到ID为 ${SPECIFIC_ID} 的案例`); |
|
|
process.exit(1); |
|
|
} |
|
|
cases = [specificCase]; |
|
|
} else { |
|
|
|
|
|
cases = getCelebrityCases(null, 100, 0); |
|
|
} |
|
|
|
|
|
total = cases.length; |
|
|
console.log(`找到 ${total} 个名人案例`); |
|
|
|
|
|
|
|
|
const categories = {}; |
|
|
cases.forEach(c => { |
|
|
const cat = c.category_cn || c.category; |
|
|
categories[cat] = (categories[cat] || 0) + 1; |
|
|
}); |
|
|
console.log('\n类别分布:'); |
|
|
Object.entries(categories).forEach(([cat, count]) => { |
|
|
console.log(` - ${cat}: ${count} 个`); |
|
|
}); |
|
|
|
|
|
|
|
|
const withAnalysis = cases.filter(c => c.analysis_data).length; |
|
|
const withoutAnalysis = total - withAnalysis; |
|
|
console.log(`\n分析状态:`); |
|
|
console.log(` - 已有分析: ${withAnalysis} 个`); |
|
|
console.log(` - 需要生成: ${withoutAnalysis} 个`); |
|
|
|
|
|
if (!FORCE_REGENERATE && withoutAnalysis === 0) { |
|
|
console.log('\n✓ 所有案例都已有分析数据,无需生成'); |
|
|
console.log(' 提示: 使用 --force 参数可强制重新生成'); |
|
|
process.exit(0); |
|
|
} |
|
|
|
|
|
console.log('\n' + '─'.repeat(60)); |
|
|
console.log('开始生成...'); |
|
|
console.log('─'.repeat(60)); |
|
|
|
|
|
const startTime = Date.now(); |
|
|
|
|
|
|
|
|
await processWithConcurrency(cases, CONCURRENCY); |
|
|
|
|
|
const totalElapsed = ((Date.now() - startTime) / 1000).toFixed(1); |
|
|
|
|
|
|
|
|
console.log('\n' + '═'.repeat(60)); |
|
|
console.log(' 生成完成'); |
|
|
console.log('═'.repeat(60)); |
|
|
console.log(`总耗时: ${totalElapsed} 秒`); |
|
|
console.log(`结果:`); |
|
|
console.log(` ✓ 成功: ${completed} 个`); |
|
|
console.log(` ✗ 失败: ${failed} 个`); |
|
|
console.log(` ⊖ 跳过: ${skipped} 个`); |
|
|
console.log('═'.repeat(60)); |
|
|
|
|
|
if (failed > 0) { |
|
|
console.log('\n⚠️ 部分案例生成失败,请检查日志后重试'); |
|
|
process.exit(1); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
main().catch(error => { |
|
|
console.error('脚本执行失败:', error); |
|
|
process.exit(1); |
|
|
}); |
|
|
|