fff77dac3f
Add liuyao/bazi/combined flows with shared calc and AI infrastructure, 64-gua learn routes, and update Ubuntu PM2 deployment docs for port 3130. Co-authored-by: Cursor <cursoragent@cursor.com>
177 lines
5.1 KiB
TypeScript
177 lines
5.1 KiB
TypeScript
import { Solar } from "lunar-javascript";
|
||
import {
|
||
adjustToTrueSolarTime,
|
||
formatDateTime,
|
||
parseDateTime,
|
||
} from "@/lib/calc/time";
|
||
|
||
export interface BaziInput {
|
||
date: string;
|
||
time: string;
|
||
gender: "male" | "female";
|
||
longitude: number;
|
||
unknownHour?: boolean;
|
||
}
|
||
|
||
export interface PillarInfo {
|
||
ganZhi: string;
|
||
shiShenGan: string;
|
||
shiShenZhi: string[];
|
||
hideGan: string[];
|
||
naYin: string;
|
||
}
|
||
|
||
export interface DaYunInfo {
|
||
ganZhi: string;
|
||
startAge: number;
|
||
}
|
||
|
||
export interface BaziChart {
|
||
birthTime: string;
|
||
trueSolarTime: string;
|
||
unknownHour: boolean;
|
||
lunarDate: string;
|
||
pillars: {
|
||
year: PillarInfo;
|
||
month: PillarInfo;
|
||
day: PillarInfo;
|
||
time: PillarInfo;
|
||
};
|
||
daYun: {
|
||
startYear: number;
|
||
startMonth: number;
|
||
startDay: number;
|
||
items: DaYunInfo[];
|
||
};
|
||
liuNian: { year: number; ganZhi: string }[];
|
||
shenSha: {
|
||
ji: string[];
|
||
xiong: string[];
|
||
};
|
||
}
|
||
|
||
function buildPillar(
|
||
ganZhi: string,
|
||
shiShenGan: string,
|
||
shiShenZhi: string[],
|
||
hideGan: string[],
|
||
naYin: string,
|
||
): PillarInfo {
|
||
return { ganZhi, shiShenGan, shiShenZhi, hideGan, naYin };
|
||
}
|
||
|
||
export function calculateBazi(input: BaziInput): BaziChart {
|
||
const localTime = parseDateTime(input.date, input.time);
|
||
const trueSolar = adjustToTrueSolarTime(localTime, input.longitude);
|
||
|
||
const solar = Solar.fromYmdHms(
|
||
trueSolar.getFullYear(),
|
||
trueSolar.getMonth() + 1,
|
||
trueSolar.getDate(),
|
||
input.unknownHour ? 12 : trueSolar.getHours(),
|
||
input.unknownHour ? 0 : trueSolar.getMinutes(),
|
||
0,
|
||
);
|
||
|
||
const lunar = solar.getLunar();
|
||
const ec = lunar.getEightChar();
|
||
const genderCode = input.gender === "male" ? 1 : 0;
|
||
const yun = ec.getYun(genderCode);
|
||
|
||
const currentYear = new Date().getFullYear();
|
||
const liuNian: { year: number; ganZhi: string }[] = [];
|
||
for (let year = currentYear - 2; year <= currentYear + 3; year++) {
|
||
const yearSolar = Solar.fromYmdHms(year, 6, 1, 12, 0, 0);
|
||
liuNian.push({
|
||
year,
|
||
ganZhi: yearSolar.getLunar().getYearInGanZhi(),
|
||
});
|
||
}
|
||
|
||
const daYunList = yun.getDaYun();
|
||
const daYunItems: DaYunInfo[] = [];
|
||
let age = yun.getStartYear();
|
||
for (const dy of daYunList) {
|
||
const gz = dy.getGanZhi();
|
||
if (!gz) {
|
||
continue;
|
||
}
|
||
daYunItems.push({ ganZhi: gz, startAge: age });
|
||
age += 10;
|
||
if (daYunItems.length >= 8) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
return {
|
||
birthTime: formatDateTime(localTime),
|
||
trueSolarTime: formatDateTime(trueSolar),
|
||
unknownHour: !!input.unknownHour,
|
||
lunarDate: lunar.toString(),
|
||
pillars: {
|
||
year: buildPillar(
|
||
ec.getYear(),
|
||
ec.getYearShiShenGan(),
|
||
ec.getYearShiShenZhi(),
|
||
ec.getYearHideGan(),
|
||
ec.getYearNaYin(),
|
||
),
|
||
month: buildPillar(
|
||
ec.getMonth(),
|
||
ec.getMonthShiShenGan(),
|
||
ec.getMonthShiShenZhi(),
|
||
ec.getMonthHideGan(),
|
||
ec.getMonthNaYin(),
|
||
),
|
||
day: buildPillar(
|
||
ec.getDay(),
|
||
ec.getDayShiShenGan(),
|
||
ec.getDayShiShenZhi(),
|
||
ec.getDayHideGan(),
|
||
ec.getDayNaYin(),
|
||
),
|
||
time: buildPillar(
|
||
input.unknownHour ? "不详" : ec.getTime(),
|
||
input.unknownHour ? "—" : ec.getTimeShiShenGan(),
|
||
input.unknownHour ? [] : ec.getTimeShiShenZhi(),
|
||
input.unknownHour ? [] : ec.getTimeHideGan(),
|
||
input.unknownHour ? "—" : ec.getTimeNaYin(),
|
||
),
|
||
},
|
||
daYun: {
|
||
startYear: yun.getStartYear(),
|
||
startMonth: yun.getStartMonth(),
|
||
startDay: yun.getStartDay(),
|
||
items: daYunItems,
|
||
},
|
||
liuNian,
|
||
shenSha: {
|
||
ji: lunar.getDayJiShen(),
|
||
xiong: lunar.getDayXiongSha(),
|
||
},
|
||
};
|
||
}
|
||
|
||
export function formatBaziForPrompt(chart: BaziChart): string {
|
||
const p = chart.pillars;
|
||
const lines = [
|
||
`出生时间:${chart.birthTime}`,
|
||
`真太阳时:${chart.trueSolarTime}${chart.unknownHour ? "(时辰不详,按中午排盘)" : ""}`,
|
||
`农历:${chart.lunarDate}`,
|
||
"",
|
||
"【四柱】",
|
||
`年柱:${p.year.ganZhi}(天干十神:${p.year.shiShenGan},地支十神:${p.year.shiShenZhi.join("、")},藏干:${p.year.hideGan.join("、")},纳音:${p.year.naYin})`,
|
||
`月柱:${p.month.ganZhi}(天干十神:${p.month.shiShenGan},地支十神:${p.month.shiShenZhi.join("、")},藏干:${p.month.hideGan.join("、")},纳音:${p.month.naYin})`,
|
||
`日柱:${p.day.ganZhi}(天干十神:${p.day.shiShenGan},地支十神:${p.day.shiShenZhi.join("、")},藏干:${p.day.hideGan.join("、")},纳音:${p.day.naYin})`,
|
||
`时柱:${p.time.ganZhi}(天干十神:${p.time.shiShenGan},地支十神:${p.time.shiShenZhi.join("、") || "—"},藏干:${p.time.hideGan.join("、") || "—"},纳音:${p.time.naYin})`,
|
||
"",
|
||
`【大运】起运 ${chart.daYun.startYear} 年 ${chart.daYun.startMonth} 月 ${chart.daYun.startDay} 天`,
|
||
chart.daYun.items.map((d) => `${d.startAge}岁起 ${d.ganZhi}`).join(" → "),
|
||
"",
|
||
`【流年】${chart.liuNian.map((l) => `${l.year}(${l.ganZhi})`).join("、")}`,
|
||
"",
|
||
`【神煞】吉神:${chart.shenSha.ji.join("、") || "无"};凶煞:${chart.shenSha.xiong.join("、") || "无"}`,
|
||
];
|
||
return lines.join("\n");
|
||
}
|