|
|
|
|
|
|
|
|
|
|
|
|
|
|
import crypto from 'crypto'; |
|
|
import { getDb, nowIso } from './database.js'; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export const computeBaziHash = (yearPillar, monthPillar, dayPillar, hourPillar) => { |
|
|
const combined = `${yearPillar}|${monthPillar}|${dayPillar}|${hourPillar}`; |
|
|
return crypto.createHash('sha256').update(combined).digest('hex').substring(0, 16); |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 || '{}'), |
|
|
|
|
|
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, |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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; |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 || [], |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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; |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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; |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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, |
|
|
}; |
|
|
|