const dimensionMeta = require("../constants/dimensions"); const { formatDisplayDateTime } = require("./date"); const dimensionMap = dimensionMeta.reduce((acc, item) => { acc[item.key] = item; return acc; }, {}); function pickValue(source, keys, fallback) { if (!source) { return fallback; } for (const key of keys) { if (Object.prototype.hasOwnProperty.call(source, key)) { const value = source[key]; if (value !== undefined && value !== null) { return value; } } } return fallback; } function mapTrendText(trend) { if (trend === "up") { return "回升"; } if (trend === "down") { return "波动"; } return "平稳"; } function normalizePillars(pillars) { if (!Array.isArray(pillars)) { return []; } return pillars .map((item) => ({ label: pickValue(item, ["label", "Label"], ""), value: pickValue(item, ["value", "Value"], "") })) .filter((item) => item.label || item.value); } function formatListText(list, separator = " · ") { if (!Array.isArray(list) || !list.length) { return ""; } return list.join(separator); } function normalizeFortunePayload(fortune) { if (!fortune) { return null; } const dimensionListRaw = pickValue(fortune, ["dimensions", "Dimensions"], []); const dimensionList = Array.isArray(dimensionListRaw) ? dimensionListRaw : []; const dimensions = dimensionList.map((item) => { const key = pickValue(item, ["key", "Key"], ""); const meta = dimensionMap[key] || {}; const trend = pickValue(item, ["trend", "Trend"], "steady"); return { key, title: pickValue(item, ["title", "Title"], meta.title || key), score: pickValue(item, ["score", "Score"], 0), trend, insight: pickValue(item, ["insight", "Insight"], ""), suggestion: pickValue(item, ["suggestion", "Suggestion"], ""), icon: meta.icon || "⭐", accent: meta.accent || "#ffb85c", trendText: mapTrendText(trend) }; }); const total = dimensions.reduce((acc, current) => acc + (current.score || 0), 0); const overallScore = dimensions.length ? Math.round(total / dimensions.length) : 0; return { fortuneDate: pickValue(fortune, ["fortuneDate", "FortuneDate"], ""), summary: pickValue(fortune, ["summary", "Summary"], ""), narrative: pickValue(fortune, ["narrative", "Narrative"], ""), dimensions, overallScore, luckyGuide: normalizeGuide(pickValue(fortune, ["luckyGuide", "LuckyGuide"], null)), profile: normalizeProfile(pickValue(fortune, ["profile", "Profile"], null)) }; } function normalizeProfile(profile) { if (!profile) { return null; } const fiveElements = pickValue(profile, ["fiveElementDistribution", "FiveElementDistribution"], []); const total = fiveElements.reduce((acc, item) => acc + (pickValue(item, ["count", "Count"], 0) || 0), 0) || 1; const distribution = fiveElements.map((item) => { const count = pickValue(item, ["count", "Count"], 0); const percent = Math.round(((count || 0) / total) * 100); return { element: pickValue(item, ["element", "Element"], "未知"), count, percent, width: `${Math.max(percent, 6)}%` }; }); const birthDateTimeRaw = pickValue(profile, ["birthDateTime", "BirthDateTime"], ""); return { birthCity: pickValue(profile, ["birthCity", "BirthCity"], ""), birthProvince: pickValue(profile, ["birthProvince", "BirthProvince"], ""), birthDateTime: birthDateTimeRaw ? formatDisplayDateTime(birthDateTimeRaw) : "", birthPillars: normalizePillars(pickValue(profile, ["birthPillars", "BirthPillars"], [])), todayPillars: normalizePillars(pickValue(profile, ["todayPillars", "TodayPillars"], [])), distribution, weakElements: pickValue(profile, ["weakElements", "WeakElements"], []), strongElements: pickValue(profile, ["strongElements", "StrongElements"], []) }; } function normalizeGuide(guide) { if (!guide) { return null; } const slots = pickValue(guide, ["bestTimeSlots", "BestTimeSlots"], []); const colors = pickValue(guide, ["colors", "Colors"], []); const directions = pickValue(guide, ["directions", "Directions"], []); const props = pickValue(guide, ["props", "Props"], []); const activities = pickValue(guide, ["activities", "Activities"], []); return { element: pickValue(guide, ["element", "Element"], "木"), colors, colorText: formatListText(colors), directions, directionText: formatListText(directions), props, propsText: formatListText(props), activities, activitiesText: formatListText(activities, " / "), bestTimeSlots: Array.isArray(slots) ? slots.map((slot) => ({ label: pickValue(slot, ["label", "Label"], "时段"), period: pickValue(slot, ["period", "Period"], ""), reason: pickValue(slot, ["reason", "Reason"], "") })) : [] }; } module.exports = { normalizeFortunePayload };