// 为雅思卡片(UserID=2, MiaoguoCardID>=1671933)的 ContentType=2 补充例句 // 例句来源: kylx365_db.Words.ExampleSentence (BookID 212-217, 每词5句) // 渲染格式参照"其余单词"卡片: 例句:\n[读]英文句[/读]\n中文翻译 import { query } from '../../../src/util/db.js'; import { stringUtils } from '../../../src/util/stringClass.js'; function extractWord(content) { // 优先从音频URL提取: gettts?...&text=regent&... let m = content.match(/text=([^&]+)/); if (m && m[1]) return decodeURIComponent(m[1]).trim(); // 兜底: "regent 的基本中文释义" m = content.match(/^(.*?)\s*的基本中文释义/); if (m && m[1]) return m[1].trim(); return null; } // 高亮单词(参照 build.test31.js / 已有雅思卡片格式: [线]word[/线]) function highlightWord(sentence, word) { const forms = stringUtils.getWordAllForms(word); let sent = sentence; for (const form of forms) { if (sent.indexOf(form) >= 0) { sent = sent.replace(form, '[线]' + form + '[/线]'); break; } } return sent; } // 渲染格式与"其余单词"雅思卡片一致: 例句:\n英文(高亮单词)\n中文\n\n... function buildExampleBlock(sentences, word) { const lines = sentences.map(s => highlightWord(s.Sentence, word) + '\n' + s.Translate); return '\n例句:\n' + lines.join('\n\n') + '\n\n'; } async function run({ dryRun = false } = {}) { // 1) 取目标卡片 const cards = await query( 'SELECT MiaoguoCardID FROM kylx365_db.MiaoguoCard where UserID=2 and MiaoguoCardID>=1671933 order by MiaoguoCardID' ); const ids = cards.map(r => r.MiaoguoCardID); console.log('目标卡片数:', ids.length); let updated = 0, skipped = 0, noWord = 0, noSrc = 0; for (const cid of ids) { const rows = await query( 'select Content from kylx365_db.MiaoguoContent where MiaoguoCardID=? and ContentType=2', [cid] ); if (!rows.length) { noWord++; console.log(' 跳过(无ContentType=2):', cid); continue; } let content = rows[0].Content; if (content.includes('例句:')) { skipped++; continue; } const word = extractWord(content); if (!word) { noWord++; console.log(' 跳过(无法提取单词):', cid); continue; } // 2) 查 Words.ExampleSentence const ws = await query( 'select ExampleSentence from kylx365_db.Words where BookID in (212,213,214,215,216,217) and Word=? limit 1', [word] ); if (!ws.length || !ws[0].ExampleSentence) { noSrc++; console.log(' 跳过(无例句源):', cid, word); continue; } let parsed; try { parsed = JSON.parse(ws[0].ExampleSentence); } catch (e) { noSrc++; console.log(' 跳过(JSON解析失败):', cid, word); continue; } const sentences = (parsed.Sentences || []).filter(s => s.Sentence && s.Translate); if (!sentences.length) { noSrc++; console.log(' 跳过(例句为空):', cid, word); continue; } const block = buildExampleBlock(sentences, word); const newContent = content.replace(/\s*$/, '') + block; if (dryRun) { console.log(`\n=== [预览] Card ${cid} word=${word} 例句数=${sentences.length} ===`); console.log(newContent.slice(0, 700)); } else { await query( 'update kylx365_db.MiaoguoContent set Content=? where MiaoguoCardID=? and ContentType=2', [newContent, cid] ); updated++; if (updated <= 2) { console.log(`\n=== [已写] Card ${cid} word=${word} 例句数=${sentences.length} ===`); console.log(newContent.slice(0, 700)); } } } console.log(`\n完成. updated=${updated} skipped(已有例句)=${skipped} noWord=${noWord} noSrc=${noSrc}`); process.exit(0); } const dryRun = process.argv.includes('--dry'); run({ dryRun }).catch(e => { console.error(e); process.exit(1); });