const DEFAULT_CONFIG = { w_view: 0.1, w_like: 3.0, w_comment: 5.0, w_collect: 4.0, decay_base: 2.0, decay_exp: 1.5, }; export function computeHotScore(stats, config = DEFAULT_CONFIG, nowMs = Date.now()) { const publishedAt = Number(stats.published_at ?? stats.publishedAt ?? nowMs); const ageHours = Math.max(0, (nowMs - publishedAt) / 3_600_000); const numerator = Number(stats.view_count ?? 0) * config.w_view + Number(stats.like_count ?? 0) * config.w_like + Number(stats.comment_count ?? 0) * config.w_comment + Number(stats.collect_count ?? 0) * config.w_collect; const denominator = Math.pow(ageHours + config.decay_base, config.decay_exp); if (denominator <= 0) return 0; return Math.max(0, numerator / denominator); } export async function loadAlgorithmConfig(pool) { const [rows] = await pool.query(`SELECT \`key\`, value FROM plaza_algorithm_config`); const config = { ...DEFAULT_CONFIG }; for (const row of rows) { if (row.key in config) config[row.key] = Number(row.value); } return config; } export async function ensureAlgorithmConfig(pool) { const [rows] = await pool.query(`SELECT COUNT(*) AS count FROM plaza_algorithm_config`); if (Number(rows[0]?.count ?? 0) > 0) return; const now = Date.now(); const seeds = [ ['w_view', 0.1, '浏览量权重'], ['w_like', 3.0, '点赞权重'], ['w_comment', 5.0, '评论权重'], ['w_collect', 4.0, '收藏权重'], ['decay_base', 2.0, '时间衰减基数'], ['decay_exp', 1.5, '时间衰减指数'], ]; for (const [key, value, description] of seeds) { await pool.query( `INSERT INTO plaza_algorithm_config (\`key\`, value, description, updated_at) VALUES (?, ?, ?, ?)`, [key, value, description, now], ); } } export async function recalculateHotScores(pool, { windowHours = 48 } = {}) { await ensureAlgorithmConfig(pool); const config = await loadAlgorithmConfig(pool); const now = Date.now(); const windowStart = now - windowHours * 3_600_000; const [rows] = await pool.query( `SELECT id, view_count, like_count, comment_count, collect_count, published_at FROM plaza_posts WHERE status = 'published' AND published_at >= ?`, [windowStart], ); if (rows.length === 0) return { updated: 0 }; const conn = await pool.getConnection(); try { await conn.beginTransaction(); for (const row of rows) { const hotScore = computeHotScore(row, config, now); await conn.query( `UPDATE plaza_posts SET hot_score = ?, hot_updated_at = ?, updated_at = ? WHERE id = ?`, [hotScore, now, now, row.id], ); } await conn.commit(); } catch (error) { await conn.rollback(); throw error; } finally { conn.release(); } return { updated: rows.length }; } export const algorithmInternals = { DEFAULT_CONFIG, computeHotScore, };