// 调试:排查首页「每周周报提醒」的判定依据 // 读取最近 3 周(W30/W31/W32)周报的真实标题/内容,并模拟前端 isWeeklyMeaningfulReport 判定 // 用法:node dev_test_scripts/debug/debug_weekly_reminder.js const path = require('path') const fs = require('fs') const os = require('os') // 复刻 index.js 的 env 加载逻辑(凭据零硬编码) const userProfile = process.env.USERPROFILE || process.env.HOME || os.homedir() const candidates = [path.join(userProfile, 'Toolbox_local_creds.env.local')] try { const usersRoot = path.join(path.parse(userProfile).root, 'Users') if (fs.existsSync(usersRoot)) { fs.readdirSync(usersRoot).forEach(name => { const dir = path.join(usersRoot, name) if (dir !== userProfile && fs.existsSync(dir)) candidates.push(path.join(dir, 'Toolbox_local_creds.env.local')) }) } } catch {} for (const p of candidates) { if (!fs.existsSync(p)) continue fs.readFileSync(p, 'utf-8').split(/\r?\n/).forEach(line => { const t = line.trim() if (!t || t.startsWith('#')) return const i = t.indexOf('=') if (i > 0) { const k = t.slice(0, i).trim() const v = t.slice(i + 1).trim() if (!process.env[k]) process.env[k] = v } }) } process.chdir(path.join(__dirname, '..', '..')) // 按 weekly.js 的启动顺序初始化 const { initCrypto } = require('../../src/server/weekly_crypto') const { initDB } = require('../../src/server/weekly_db') const cfgPath = path.join(process.cwd(), 'config', 'weekly.json') let machineSalt = 'salt' try { machineSalt = JSON.parse(fs.readFileSync(cfgPath, 'utf-8')).machine_salt || 'salt' } catch {} initCrypto(process.env.SYSTEM_AUTH_PASS_93220 || process.env.WEEKLY_PASSWORD || '', machineSalt) initDB(path.join(process.cwd(), 'data', 'weekly.db'), path.join(process.cwd(), 'data', 'weekly.enc')) const core = require('../../src/server/weekly_core') // 与 public/index.js 的 isWeeklyAutoTitle / isWeeklyMeaningfulReport 保持一致 const TEMPLATE_TEXT = '' // 前端 getWeeklyTemplatePlainText 的纯文本,这里只做关键项判断 const isAutoTitle = (title, weekId) => { const t = String(title || '').trim() if (!t) return true if (t.includes('(自动新增)')) return true if (weekId && t === `${weekId} 周报`) return true return false } const isMeaningful = (report, weekId) => { if (!report) return { ok: false, reason: '无周报记录' } const title = String(report.title || '').trim() const content = String(report.content || '').trim() if (!title || !content) return { ok: false, reason: '标题或内容为空' } if (isAutoTitle(title, weekId)) return { ok: false, reason: '标题仍为自动标题(含「自动新增」或等于「 周报」)' } if (content === '这是自动新增的周报。你可以修改。') return { ok: false, reason: '内容仍为自动占位文本' } return { ok: true, reason: '' } } for (const w of ['2026-W30', '2026-W31', '2026-W32']) { const r = core.getReportByWeek(w) if (!r) { console.log(`\n[${w}] 无周报记录 => 提醒判定:缺`) continue } const judge = isMeaningful(r, w) console.log(`\n[${w}] id=${r.id} updated_at=${r.updated_at}`) console.log(` 标题: ${JSON.stringify(r.title)}`) console.log(` 内容前80字: ${JSON.stringify(String(r.content || '').replace(/\s+/g, ' ').slice(0, 80))}`) console.log(` 内容长度: ${String(r.content || '').length}`) console.log(` 提醒判定: ${judge.ok ? '已写(不算缺)' : '缺(' + judge.reason + ')'}`) } // ---- 追加排查:reports 全表时间线 + W31/W32 下的 events + 相关日志 ---- const { getDB } = require('../../src/server/weekly_db') const { dec } = require('../../src/server/weekly_crypto') const db = getDB() console.log('\n===== reports 时间线(最近 10 条) =====') db.prepare(`SELECT id, week_id, created_at, updated_at FROM reports ORDER BY id DESC LIMIT 10`).all() .forEach(r => console.log(` id=${r.id} week=${r.week_id} created=${r.created_at} updated=${r.updated_at}`)) for (const rid of [149, 150]) { console.log(`\n===== report ${rid} 下的 events =====`) const evs = db.prepare(`SELECT id, title_enc, title_iv, title_tag, deadline, created_at, updated_at FROM events WHERE report_id=? ORDER BY id`).all(rid) if (evs.length === 0) console.log(' (无 events)') evs.forEach(e => console.log(` ev_id=${e.id} title=${JSON.stringify(dec(e.title_enc, e.title_iv, e.title_tag))} deadline=${e.deadline} created=${e.created_at} updated=${e.updated_at}`)) } console.log('\n===== logs 中 fitness/ext 相关(最近 15 条) =====') try { db.prepare(`SELECT action, details, created_at FROM logs WHERE action LIKE '%ext%' OR action LIKE '%fitness%' ORDER BY id DESC LIMIT 15`).all() .forEach(l => console.log(` ${l.created_at} ${l.action} ${String(l.details || '').slice(0, 120)}`)) } catch (e) { console.log(' logs 查询失败:', e.message) }