import type { CalcHistoryCreate, CalcHistoryEntry } from "@/lib/history/types"; async function parseApiError(res: Response): Promise { try { const data = (await res.json()) as { error?: string }; return data.error ?? `请求失败 (${res.status})`; } catch { return `请求失败 (${res.status})`; } } export async function loadHistory(): Promise { const res = await fetch("/api/history", { cache: "no-store" }); if (!res.ok) { throw new Error(await parseApiError(res)); } const data = (await res.json()) as { items: CalcHistoryEntry[] }; return data.items ?? []; } export async function saveHistoryEntry( entry: CalcHistoryCreate, ): Promise { const res = await fetch("/api/history", { method: "POST", cache: "no-store", headers: { "Content-Type": "application/json" }, body: JSON.stringify(entry), }); if (!res.ok) { throw new Error(await parseApiError(res)); } const data = (await res.json()) as { entry: CalcHistoryEntry }; return data.entry; } export async function saveHistoryEntrySafe( entry: CalcHistoryCreate, ): Promise< { ok: true; entry: CalcHistoryEntry } | { ok: false; error: string } > { try { const saved = await saveHistoryEntry(entry); return { ok: true, entry: saved }; } catch (err) { return { ok: false, error: err instanceof Error ? err.message : String(err), }; } } export async function deleteHistoryEntry(id: string): Promise { const res = await fetch(`/api/history/${encodeURIComponent(id)}`, { method: "DELETE", cache: "no-store", }); if (!res.ok) { throw new Error(await parseApiError(res)); } } export function downloadMarkdown(content: string, filename: string) { const blob = new Blob([content], { type: "text/markdown;charset=utf-8" }); const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = filename.endsWith(".md") ? filename : `${filename}.md`; a.click(); URL.revokeObjectURL(url); } function formatBaziInputLines(entry: CalcHistoryEntry): string[] { if (!entry.baziInput) { return []; } const { baziInput } = entry; const gender = baziInput.gender === "male" ? "男" : "女"; const hour = baziInput.unknownHour ? "时辰不详" : baziInput.time; return [ `- 出生地域:${baziInput.birthPlaceName}`, `- 阳历生日:${baziInput.date} ${hour}`, `- 性别:${gender}`, ]; } function formatBaziChartLines(entry: CalcHistoryEntry): string[] { if (!entry.baziChart) { return []; } const { baziChart } = entry; const { pillars } = baziChart; return [ `- 农历:${baziChart.lunarDate}`, `- 四柱:${pillars.year.ganZhi} ${pillars.month.ganZhi} ${pillars.day.ganZhi} ${pillars.time.ganZhi}`, `- 真太阳时:${baziChart.trueSolarTime}`, ]; } export function buildHistoryMarkdown(entry: CalcHistoryEntry): string { const lines = [ `# ${entry.title}`, "", `- 类型:${entry.mode}`, `- 时间:${new Date(entry.createdAt).toLocaleString("zh-CN")}`, `- 问事:${entry.question}`, "", ...formatBaziInputLines(entry), ...formatBaziChartLines(entry), ...Object.entries(entry.meta) .filter(([key]) => !["出生地域", "阳历生日", "农历", "性别"].includes(key)) .map(([k, v]) => `- ${k}:${v}`), ]; if (entry.hexagram) { lines.push( `- 卦象:${entry.hexagram.guaTitle}`, `- 卦辞:${entry.hexagram.guaResult}`, ); } lines.push("", "---", "", entry.completion); return lines.join("\n"); }