/** * 八字分析缓存管理器 * 实现永久缓存策略,同一八字返回相同核心结论 */ import crypto from 'crypto'; import { getDb, nowIso } from './database.js'; /** * 计算八字哈希值 * @param {string} yearPillar - 年柱 * @param {string} monthPillar - 月柱 * @param {string} dayPillar - 日柱 * @param {string} hourPillar - 时柱 * @returns {string} 16位哈希值 */ export const computeBaziHash = (yearPillar, monthPillar, dayPillar, hourPillar) => { const combined = `${yearPillar}|${monthPillar}|${dayPillar}|${hourPillar}`; return crypto.createHash('sha256').update(combined).digest('hex').substring(0, 16); }; /** * 获取缓存的核心分析结果 * @param {string} baziHash - 八字哈希 * @param {string} gender - 性别 (male/female) * @returns {object|null} 缓存的分析结果或null */ export const getCachedAnalysis = (baziHash, gender) => { const db = getDb(); const stmt = db.prepare(` SELECT * FROM bazi_analysis_cache WHERE bazi_hash = ? AND gender = ? `); const row = stmt.get(baziHash, gender); if (!row) return null; return { id: row.id, baziHash: row.bazi_hash, gender: row.gender, // 结构数据 structuralData: JSON.parse(row.structural_data || '{}'), // 命理骨架 personalityCore: JSON.parse(row.personality_core || '{}'), careerCore: JSON.parse(row.career_core || '{}'), wealthCore: JSON.parse(row.wealth_core || '{}'), marriageCore: JSON.parse(row.marriage_core || '{}'), healthCore: JSON.parse(row.health_core || '{}'), // K线数据 klineData: JSON.parse(row.kline_data || '[]'), peakYears: JSON.parse(row.peak_years || '[]'), troughYears: JSON.parse(row.trough_years || '[]'), // 新增分析维度 cryptoCore: JSON.parse(row.crypto_core || '{}'), monthlyFortune: JSON.parse(row.monthly_fortune || '{}'), yearlyFortune: JSON.parse(row.yearly_fortune || '{}'), luckyElements: JSON.parse(row.lucky_elements || '{}'), physicalTraits: JSON.parse(row.physical_traits || '{}'), keyDates: JSON.parse(row.key_dates || '{}'), pastEvents: JSON.parse(row.past_events || '[]'), futureEvents: JSON.parse(row.future_events || '[]'), // 元数据 modelUsed: row.model_used, version: row.version, createdAt: row.created_at, }; }; /** * 保存分析结果到缓存 * @param {object} data - 分析数据 */ export const cacheAnalysis = (data) => { const db = getDb(); const id = `cache_${data.baziHash}_${data.gender}_${Date.now()}`; const stmt = db.prepare(` INSERT OR REPLACE INTO bazi_analysis_cache ( id, bazi_hash, gender, structural_data, personality_core, career_core, wealth_core, marriage_core, health_core, kline_data, peak_years, trough_years, crypto_core, monthly_fortune, yearly_fortune, lucky_elements, physical_traits, key_dates, past_events, future_events, model_used, version, created_at ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) `); stmt.run( id, data.baziHash, data.gender, JSON.stringify(data.structuralData || {}), JSON.stringify(data.personalityCore || {}), JSON.stringify(data.careerCore || {}), JSON.stringify(data.wealthCore || {}), JSON.stringify(data.marriageCore || {}), JSON.stringify(data.healthCore || {}), JSON.stringify(data.klineData || []), JSON.stringify(data.peakYears || []), JSON.stringify(data.troughYears || []), JSON.stringify(data.cryptoCore || {}), JSON.stringify(data.monthlyFortune || {}), JSON.stringify(data.yearlyFortune || {}), JSON.stringify(data.luckyElements || {}), JSON.stringify(data.physicalTraits || {}), JSON.stringify(data.keyDates || {}), JSON.stringify(data.pastEvents || []), JSON.stringify(data.futureEvents || []), data.modelUsed || 'unknown', data.version || 1, nowIso() ); return id; }; /** * 从完整分析结果中提取核心数据用于缓存 * @param {object} analysisResult - AI返回的完整分析结果 * @param {object} chartData - K线数据 * @returns {object} 核心缓存数据 */ export const extractCoreData = (analysisResult, chartData) => { // 找出巅峰年和低谷年 const sortedByScore = [...(chartData || [])].sort((a, b) => b.score - a.score); const peakYears = sortedByScore.slice(0, 5).map(p => ({ year: p.year, age: p.age, score: p.score })); const troughYears = sortedByScore.slice(-5).reverse().map(p => ({ year: p.year, age: p.age, score: p.score })); return { structuralData: { bazi: analysisResult.bazi, summaryScore: analysisResult.summaryScore, }, personalityCore: { content: analysisResult.personality, score: analysisResult.personalityScore, }, careerCore: { content: analysisResult.industry, score: analysisResult.industryScore, }, wealthCore: { content: analysisResult.wealth, score: analysisResult.wealthScore, }, marriageCore: { content: analysisResult.marriage, score: analysisResult.marriageScore, }, healthCore: { content: analysisResult.health, score: analysisResult.healthScore, bodyParts: analysisResult.healthBodyParts || [], }, klineData: chartData, peakYears, troughYears, cryptoCore: { content: analysisResult.crypto, score: analysisResult.cryptoScore, cryptoYear: analysisResult.cryptoYear, cryptoStyle: analysisResult.cryptoStyle, }, monthlyFortune: { content: analysisResult.monthlyFortune, highlights: analysisResult.monthlyHighlights || [], }, yearlyFortune: { content: analysisResult.yearlyFortune, keyEvents: analysisResult.yearlyKeyEvents || [], }, luckyElements: { colors: analysisResult.luckyColors || [], directions: analysisResult.luckyDirections || [], zodiac: analysisResult.luckyZodiac || [], numbers: analysisResult.luckyNumbers || [], }, physicalTraits: { appearance: analysisResult.appearance, bodyType: analysisResult.bodyType, skin: analysisResult.skin, characterSummary: analysisResult.characterSummary, }, keyDates: { thisYear: analysisResult.keyDatesThisYear || [], thisMonth: analysisResult.keyDatesThisMonth || [], }, pastEvents: analysisResult.pastEvents || [], futureEvents: analysisResult.futureEvents || [], }; }; /** * 合并缓存数据和新生成的润色文字 * @param {object} cachedData - 缓存的核心数据 * @param {object} freshPolish - 新生成的文字润色(可选) * @returns {object} 最终分析结果 */ export const mergeCachedWithFresh = (cachedData, freshPolish = null) => { // 如果没有新润色,直接返回缓存数据构建的结果 const result = { bazi: cachedData.structuralData?.bazi || [], summary: freshPolish?.summary || cachedData.structuralData?.summary || '命理分析已从缓存加载', summaryScore: cachedData.structuralData?.summaryScore || 5, personality: cachedData.personalityCore?.content || '', personalityScore: cachedData.personalityCore?.score || 5, industry: cachedData.careerCore?.content || '', industryScore: cachedData.careerCore?.score || 5, wealth: cachedData.wealthCore?.content || '', wealthScore: cachedData.wealthCore?.score || 5, marriage: cachedData.marriageCore?.content || '', marriageScore: cachedData.marriageCore?.score || 5, health: cachedData.healthCore?.content || '', healthScore: cachedData.healthCore?.score || 5, healthBodyParts: cachedData.healthCore?.bodyParts || [], family: freshPolish?.family || cachedData.familyCore?.content || '', familyScore: cachedData.familyCore?.score || 5, fengShui: freshPolish?.fengShui || cachedData.fengShuiCore?.content || '', fengShuiScore: cachedData.fengShuiCore?.score || 5, crypto: cachedData.cryptoCore?.content || '', cryptoScore: cachedData.cryptoCore?.score || 5, cryptoYear: cachedData.cryptoCore?.cryptoYear || '待定', cryptoStyle: cachedData.cryptoCore?.cryptoStyle || '现货定投', // 新增字段 monthlyFortune: cachedData.monthlyFortune?.content || '', monthlyHighlights: cachedData.monthlyFortune?.highlights || [], yearlyFortune: cachedData.yearlyFortune?.content || '', yearlyKeyEvents: cachedData.yearlyFortune?.keyEvents || [], luckyColors: cachedData.luckyElements?.colors || [], luckyDirections: cachedData.luckyElements?.directions || [], luckyZodiac: cachedData.luckyElements?.zodiac || [], luckyNumbers: cachedData.luckyElements?.numbers || [], appearance: cachedData.physicalTraits?.appearance || '', bodyType: cachedData.physicalTraits?.bodyType || '', skin: cachedData.physicalTraits?.skin || '', characterSummary: cachedData.physicalTraits?.characterSummary || '', keyDatesThisYear: cachedData.keyDates?.thisYear || [], keyDatesThisMonth: cachedData.keyDates?.thisMonth || [], pastEvents: cachedData.pastEvents || [], futureEvents: cachedData.futureEvents || [], peakYears: cachedData.peakYears || [], troughYears: cachedData.troughYears || [], }; return result; }; /** * 检查缓存是否存在 * @param {string} baziHash - 八字哈希 * @param {string} gender - 性别 * @returns {boolean} */ export const hasCachedAnalysis = (baziHash, gender) => { const db = getDb(); const stmt = db.prepare(` SELECT COUNT(*) as count FROM bazi_analysis_cache WHERE bazi_hash = ? AND gender = ? `); const row = stmt.get(baziHash, gender); return row.count > 0; }; /** * 获取缓存统计信息 * @returns {object} 缓存统计 */ export const getCacheStats = () => { const db = getDb(); const totalStmt = db.prepare('SELECT COUNT(*) as count FROM bazi_analysis_cache'); const total = totalStmt.get().count; const recentStmt = db.prepare(` SELECT COUNT(*) as count FROM bazi_analysis_cache WHERE created_at > datetime('now', '-24 hours') `); const recent = recentStmt.get().count; return { total, recentlyAdded: recent }; }; export default { computeBaziHash, getCachedAnalysis, cacheAnalysis, extractCoreData, mergeCachedWithFresh, hasCachedAnalysis, getCacheStats, };