verify-model-switch.cjs 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. // e2e-verify-model-switch.js
  2. // 实际数据库连接,验证扫描器/状态机/供应商切换
  3. const { PrismaClient } = require('@prisma/client');
  4. // 从 server/.env 加载 DATABASE_URL
  5. const fs = require('fs');
  6. const path = require('path');
  7. try {
  8. const envPath = path.resolve(__dirname, '../../server/.env');
  9. const envContent = fs.readFileSync(envPath, 'utf-8');
  10. for (const line of envContent.split('\n')) {
  11. const m = line.match(/^([A-Z_][A-Z0-9_]*)=(.*)$/);
  12. if (m && !process.env[m[1]]) {
  13. process.env[m[1]] = m[2].replace(/^["']|["']$/g, '');
  14. }
  15. }
  16. } catch (e) {
  17. console.warn('未找到 .env, 依靠环境变量');
  18. }
  19. const p = new PrismaClient();
  20. (async () => {
  21. try {
  22. // 1. 找到当前数据库所有供应商状态
  23. console.log('=== 1. 当前供应商状态 ===');
  24. const recentBooks = await p.book.findMany({
  25. where: { createdAt: { gte: new Date(Date.now() - 60 * 60 * 1000) } },
  26. select: { id: true, title: true, genStage: true, errorMsg: true, updatedAt: true, createdAt: true },
  27. orderBy: { createdAt: 'desc' },
  28. take: 10,
  29. });
  30. if (recentBooks.length === 0) {
  31. console.log(' 没有最近 1 小时的书,等待真实数据...');
  32. // 看最近 24 小时的
  33. const allBooks = await p.book.findMany({
  34. select: { id: true, title: true, genStage: true, errorMsg: true, updatedAt: true, createdAt: true },
  35. orderBy: { createdAt: 'desc' },
  36. take: 10,
  37. });
  38. console.log(` 最近 10 本书 (24h+):`);
  39. for (const b of allBooks) {
  40. console.log(` - #${b.id} "${b.title.slice(0, 30)}..." stage=${b.genStage} err=${b.errorMsg ? b.errorMsg.slice(0, 80) : 'null'}`);
  41. }
  42. } else {
  43. console.log(` 最近 1 小时 ${recentBooks.length} 本书:`);
  44. for (const b of recentBooks) {
  45. console.log(` - #${b.id} "${b.title.slice(0, 30)}..." stage=${b.genStage} err=${b.errorMsg ? b.errorMsg.slice(0, 80) : 'null'}`);
  46. }
  47. }
  48. // 2. 检查 failed 章节情况
  49. console.log('\n=== 2. 失败章节统计 ===');
  50. const failedChapters = await p.bookChapter.groupBy({
  51. by: ['genStage'],
  52. _count: { genStage: true },
  53. where: { contentError: { not: null } },
  54. });
  55. for (const f of failedChapters) {
  56. console.log(` ${f.genStage}: ${f._count.genStage}`);
  57. }
  58. // 3. 检查 scan log 来推断恢复情况
  59. console.log('\n=== 3. 看最近 5 个 failed 章节的内容错误 ===');
  60. const failedSamples = await p.bookChapter.findMany({
  61. where: { contentError: { not: null } },
  62. select: { id: true, bookId: true, title: true, contentError: true, updatedAt: true },
  63. orderBy: { updatedAt: 'desc' },
  64. take: 5,
  65. });
  66. for (const c of failedSamples) {
  67. console.log(` ch#${c.id} (book ${c.bookId}) "${c.title.slice(0, 30)}":`);
  68. console.log(` err: ${(c.contentError || '').slice(0, 200)}`);
  69. console.log(` updatedAt: ${c.updatedAt.toISOString()}`);
  70. }
  71. // 4. 验证 recovery scanner 是否在工作
  72. console.log('\n=== 4. 检查扫描器状态 ===');
  73. const stuckBooks = await p.book.count({
  74. where: {
  75. genStage: 'content_generating',
  76. updatedAt: { lt: new Date(Date.now() - 30 * 60 * 1000) }, // 30分钟没动
  77. },
  78. });
  79. console.log(` 卡在 content_generating 超过 30 分钟的书: ${stuckBooks}`);
  80. const outlineReadyBooks = await p.book.count({
  81. where: {
  82. genStage: 'outline_ready',
  83. updatedAt: { lt: new Date(Date.now() - 15 * 60 * 1000) }, // 15分钟没动
  84. },
  85. });
  86. console.log(` 卡在 outline_ready 超过 15 分钟的书: ${outlineReadyBooks}`);
  87. // 5. 健康总结
  88. console.log('\n=== 5. 健康总结 ===');
  89. const healthCheck = {
  90. noCrash: true, // 如果到这里没崩就算
  91. recoverableBooks: 0,
  92. totalFailedChapters: failedSamples.length,
  93. stuckBooks,
  94. has429Evidence: false,
  95. };
  96. // 检测是否有过 429
  97. for (const c of failedSamples) {
  98. if (c.contentError && /429|quota|usage|rate limit|insufficient/i.test(c.contentError)) {
  99. healthCheck.has429Evidence = true;
  100. break;
  101. }
  102. }
  103. console.log(` 进程存活: ✅`);
  104. console.log(` 失败章节总数: ${healthCheck.totalFailedChapters}`);
  105. console.log(` 卡住的书籍: ${healthCheck.stuckBooks}`);
  106. console.log(` 看到过 429 证据: ${healthCheck.has429Evidence ? '⚠️ 是' : '✅ 否'}`);
  107. } catch (err) {
  108. console.error('❌ 测试异常:', err.message);
  109. process.exit(1);
  110. } finally {
  111. await p.$disconnect();
  112. }
  113. })();