feat: 新增免费演出雷达工具与定投评估功能,优化周报提醒逻辑
1. 新增免费演出雷达工具:含秀动/猫眼/大麦平台抓取、本地代理鉴权配置 2. 新增建行定投评估模块:四大指数PE-TTM估值与定投档位建议 3. 新增推送告警白名单过滤功能,限制推送指定指数告警 4. 重构周报首页提醒逻辑,移至服务端处理并减少前端请求 5. 新增多套调试探针脚本与单元测试用例
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
// 调试:排查首页「每周周报提醒」的判定依据
|
||||
// 读取最近 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: '标题仍为自动标题(含「自动新增」或等于「<weekId> 周报」)' }
|
||||
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) }
|
||||
Reference in New Issue
Block a user