/**
* 八字计算工具
* 用于八字的排盘和计算
*/
const Logger = require('./logger.js');
const DateUtils = require('./date-utils.js');
class BaziCalculator {
constructor() {
this.logger = new Logger();
this.dateUtils = new DateUtils();
// 初始化BaziDatabase以支持getTenGod方法
const BaziDatabase = require('../data/bazi-database.js');
this.baziDB = new BaziDatabase();
// 天干
this.heavenlyStems = [
'甲', '乙', '丙', '丁', '戊', '己', '庚', '辛', '壬', '癸'
];
// 地支
this.earthlyBranches = [
'子', '丑', '寅', '卯', '辰', '巳', '午', '未', '申', '酉', '戌', '亥'
];
// 五行属性
this.stemElements = {
'甲': '木', '乙': '木', '丙': '火', '丁': '火', '戊': '土',
'己': '土', '庚': '金', '辛': '金', '壬': '水', '癸': '水'
};
this.branchElements = {
'子': '水', '丑': '土', '寅': '木', '卯': '木', '辰': '土', '巳': '火',
'午': '火', '未': '土', '申': '金', '酉': '金', '戌': '土', '亥': '水'
};
// 阴阳属性
this.stemPolarity = {
'甲': '阳', '乙': '阴', '丙': '阳', '丁': '阴', '戊': '阳',
'己': '阴', '庚': '阳', '辛': '阴', '壬': '阳', '癸': '阴'
};
this.branchPolarity = {
'子': '阳', '丑': '阴', '寅': '阳', '卯': '阴', '辰': '阳', '巳': '阴',
'午': '阳', '未': '阴', '申': '阳', '酉': '阴', '戌': '阳', '亥': '阴'
};
// 十神关系
this.tenGods = {
'比肩': '同我',
'劫财': '同我',
'食神': '我生',
'伤官': '我生',
'偏财': '我克',
'正财': '我克',
'七杀': '克我',
'正官': '克我',
'偏印': '生我',
'正印': '生我'
};
}
/**
* 计算八字
* @param {string} birthDate - 出生日期 (YYYY-MM-DD)
* @param {string} birthTime - 出生时间 (HH:mm)
* @param {string} timezone - 时区
* @param {string} gender - 性别 (male/female)
* @param {boolean} isLunar - 是否农历
* @returns {Object} 八字数据
*/
calculateBazi(birthDate, birthTime, timezone = 'Asia/Shanghai', gender = 'male', isLunar = false) {
this.logger.info('开始计算八字', { birthDate, birthTime, timezone, gender, isLunar });
try {
// 转换为公历日期时间
const solarDateTime = isLunar ?
this.dateUtils.lunarToSolar(birthDate, birthTime) :
new Date(`${birthDate}T${birthTime}:00`);
// 计算年柱
const yearPillar = this.calculateYearPillar(solarDateTime);
// 计算月柱
const monthPillar = this.calculateMonthPillar(solarDateTime, yearPillar.stem);
// 计算日柱
const dayPillar = this.calculateDayPillar(solarDateTime);
// 计算时柱
const hourPillar = this.calculateHourPillar(solarDateTime, dayPillar.stem);
// 计算大运
const majorLuck = this.calculateMajorLuck(yearPillar, monthPillar, gender, solarDateTime);
// 计算流年
const annualLuck = this.calculateAnnualLuck(solarDateTime);
// 分析五行强弱
const elementAnalysis = this.analyzeElements([yearPillar, monthPillar, dayPillar, hourPillar]);
// 分析十神关系
const tenGodsAnalysis = this.analyzeTenGods(dayPillar.stem, [yearPillar, monthPillar, dayPillar, hourPillar]);
// 分析格局
const patternAnalysis = this.analyzePattern([yearPillar, monthPillar, dayPillar, hourPillar], elementAnalysis);
const bazi = {
birth_info: {
solar_date: solarDateTime.toISOString().split('T')[0],
solar_time: solarDateTime.toTimeString().split(' ')[0].substring(0, 5),
lunar_date: isLunar ? birthDate : this.dateUtils.solarToLunar(solarDateTime),
timezone: timezone,
gender: gender,
is_lunar: isLunar
},
four_pillars: {
year: yearPillar,
month: monthPillar,
day: dayPillar,
hour: hourPillar
},
major_luck: majorLuck,
annual_luck: annualLuck,
element_analysis: elementAnalysis,
ten_gods_analysis: tenGodsAnalysis,
pattern_analysis: patternAnalysis,
timestamp: new Date().toISOString()
};
this.logger.info('八字计算完成', { bazi });
return bazi;
} catch (error) {
this.logger.error('八字计算失败', { error: error.message, birthDate, birthTime });
throw error;
}
}
/**
* 计算年柱
* @param {Date} date - 日期
* @returns {Object} 年柱
*/
calculateYearPillar(date) {
// 立春为年的分界点
const year = date.getFullYear();
const springStart = this.getSpringStart(year);
let actualYear = year;
if (date < springStart) {
actualYear = year - 1;
}
// 计算干支
// 甲子年为1984年,每60年一个循环
const baseYear = 1984;
const yearOffset = (actualYear - baseYear) % 60;
const stemIndex = yearOffset % 10;
const branchIndex = yearOffset % 12;
const stem = this.heavenlyStems[stemIndex < 0 ? stemIndex + 10 : stemIndex];
const branch = this.earthlyBranches[branchIndex < 0 ? branchIndex + 12 : branchIndex];
return {
stem: stem,
branch: branch,
element: this.stemElements[stem],
polarity: this.stemPolarity[stem],
branch_element: this.branchElements[branch],
branch_polarity: this.branchPolarity[branch],
year: actualYear
};
}
/**
* 计算月柱
* @param {Date} date - 日期
* @param {string} yearStem - 年干
* @returns {Object} 月柱
*/
calculateMonthPillar(date, yearStem) {
const month = date.getMonth() + 1;
const day = date.getDate();
// 确定节气月份
const solarMonth = this.getSolarMonth(month, day);
// 月支固定:寅月(立春)、卯月(惊蛰)、辰月(清明)...
const monthBranches = ['寅', '卯', '辰', '巳', '午', '未', '申', '酉', '戌', '亥', '子', '丑'];
const branchIndex = (solarMonth - 1) % 12;
const branch = monthBranches[branchIndex];
// 月干根据年干推算
const yearStemIndex = this.heavenlyStems.indexOf(yearStem);
const monthStemIndex = (yearStemIndex * 2 + solarMonth - 1) % 10;
const stem = this.heavenlyStems[monthStemIndex];
return {
stem: stem,
branch: branch,
element: this.stemElements[stem],
polarity: this.stemPolarity[stem],
branch_element: this.branchElements[branch],
branch_polarity: this.branchPolarity[branch],
solar_month: solarMonth
};
}
/**
* 计算日柱
* @param {Date} date - 日期
* @returns {Object} 日柱
*/
calculateDayPillar(date) {
// 使用儒略日计算
const julianDay = this.getJulianDay(date);
// 甲子日的儒略日为1924681(1900年1月1日)
const baseJulianDay = 1924681;
const dayOffset = (julianDay - baseJulianDay) % 60;
const stemIndex = dayOffset % 10;
const branchIndex = dayOffset % 12;
const stem = this.heavenlyStems[stemIndex < 0 ? stemIndex + 10 : stemIndex];
const branch = this.earthlyBranches[branchIndex < 0 ? branchIndex + 12 : branchIndex];
return {
stem: stem,
branch: branch,
element: this.stemElements[stem],
polarity: this.stemPolarity[stem],
branch_element: this.branchElements[branch],
branch_polarity: this.branchPolarity[branch],
julian_day: julianDay
};
}
/**
* 计算时柱
* @param {Date} date - 日期
* @param {string} dayStem - 日干
* @returns {Object} 时柱
*/
calculateHourPillar(date, dayStem) {
const hour = date.getHours();
// 确定时辰
const hourBranches = ['子', '丑', '寅', '卯', '辰', '巳', '午', '未', '申', '酉', '戌', '亥'];
let hourIndex;
if (hour >= 23 || hour < 1) hourIndex = 0; // 子时 23:00-01:00
else if (hour >= 1 && hour < 3) hourIndex = 1; // 丑时 01:00-03:00
else if (hour >= 3 && hour < 5) hourIndex = 2; // 寅时 03:00-05:00
else if (hour >= 5 && hour < 7) hourIndex = 3; // 卯时 05:00-07:00
else if (hour >= 7 && hour < 9) hourIndex = 4; // 辰时 07:00-09:00
else if (hour >= 9 && hour < 11) hourIndex = 5; // 巳时 09:00-11:00
else if (hour >= 11 && hour < 13) hourIndex = 6; // 午时 11:00-13:00
else if (hour >= 13 && hour < 15) hourIndex = 7; // 未时 13:00-15:00
else if (hour >= 15 && hour < 17) hourIndex = 8; // 申时 15:00-17:00
else if (hour >= 17 && hour < 19) hourIndex = 9; // 酉时 17:00-19:00
else if (hour >= 19 && hour < 21) hourIndex = 10; // 戌时 19:00-21:00
else hourIndex = 11; // 亥时 21:00-23:00
const branch = hourBranches[hourIndex];
// 时干根据日干推算
const dayStemIndex = this.heavenlyStems.indexOf(dayStem);
const hourStemIndex = (dayStemIndex * 2 + hourIndex) % 10;
const stem = this.heavenlyStems[hourStemIndex];
return {
stem: stem,
branch: branch,
element: this.stemElements[stem],
polarity: this.stemPolarity[stem],
branch_element: this.branchElements[branch],
branch_polarity: this.branchPolarity[branch],
hour_index: hourIndex,
hour_range: this.getHourRange(hourIndex)
};
}
/**
* 计算大运
* @param {Object} yearPillar - 年柱
* @param {Object} monthPillar - 月柱
* @param {string} gender - 性别
* @param {Date} birthDate - 出生日期
* @returns {Object} 大运信息
*/
calculateMajorLuck(yearPillar, monthPillar, gender, birthDate) {
// 判断顺逆
const yearStemIndex = this.heavenlyStems.indexOf(yearPillar.stem);
const isYangYear = yearStemIndex % 2 === 0;
const isMale = gender === 'male';
// 阳年男命、阴年女命顺行,阴年男命、阳年女命逆行
const isForward = (isYangYear && isMale) || (!isYangYear && !isMale);
// 计算起运年龄(简化计算)
const startAge = this.calculateLuckStartAge(birthDate, monthPillar, isForward);
// 生成大运
const majorLuckPeriods = [];
const monthStemIndex = this.heavenlyStems.indexOf(monthPillar.stem);
const monthBranchIndex = this.earthlyBranches.indexOf(monthPillar.branch);
for (let i = 0; i < 8; i++) {
const ageStart = startAge + i * 10;
const ageEnd = ageStart + 9;
let stemIndex, branchIndex;
if (isForward) {
stemIndex = (monthStemIndex + i + 1) % 10;
branchIndex = (monthBranchIndex + i + 1) % 12;
} else {
stemIndex = (monthStemIndex - i - 1 + 10) % 10;
branchIndex = (monthBranchIndex - i - 1 + 12) % 12;
}
const stem = this.heavenlyStems[stemIndex];
const branch = this.earthlyBranches[branchIndex];
majorLuckPeriods.push({
period: i + 1,
age_start: ageStart,
age_end: ageEnd,
stem: stem,
branch: branch,
element: this.stemElements[stem],
branch_element: this.branchElements[branch]
});
}
return {
is_forward: isForward,
start_age: startAge,
periods: majorLuckPeriods
};
}
/**
* 获取天干对应的五行
* @param {string} gan - 天干
* @returns {string} 五行
*/
getGanElement(gan) {
return this.stemElements[gan] || '未知';
}
/**
* 获取天干的阴阳属性
* @param {string} gan - 天干
* @returns {string} 阴阳属性
*/
getGanYinYang(gan) {
return this.stemPolarity[gan] || '未知';
}
/**
* 生成大运序列
* @param {Object} monthPillar - 月柱
* @param {boolean} isForward - 是否顺行
* @param {number} startAge - 起运年龄
* @returns {Array} 大运序列
*/
generateMajorLuckSequence(monthPillar, isForward, startAge) {
const sequence = [];
let currentGan = monthPillar.heavenly_stem;
let currentZhi = monthPillar.earthly_branch;
for (let i = 0; i < 8; i++) {
const age = startAge + i * 10;
if (isForward) {
currentGan = this.getNextGan(currentGan);
currentZhi = this.getNextZhi(currentZhi);
} else {
currentGan = this.getPrevGan(currentGan);
currentZhi = this.getPrevZhi(currentZhi);
}
sequence.push({
period: i + 1,
age_range: `${age}-${age + 9}`,
gan: currentGan,
zhi: currentZhi,
gan_zhi: currentGan + currentZhi,
element: this.getGanElement(currentGan),
yin_yang: this.getGanYinYang(currentGan)
});
}
return sequence;
}
/**
* 获取下一个天干
* @param {string} gan - 当前天干
* @returns {string} 下一个天干
*/
getNextGan(gan) {
const gans = ['甲', '乙', '丙', '丁', '戊', '己', '庚', '辛', '壬', '癸'];
const index = gans.indexOf(gan);
return gans[(index + 1) % 10];
}
/**
* 获取上一个天干
* @param {string} gan - 当前天干
* @returns {string} 上一个天干
*/
getPrevGan(gan) {
const gans = ['甲', '乙', '丙', '丁', '戊', '己', '庚', '辛', '壬', '癸'];
const index = gans.indexOf(gan);
return gans[(index - 1 + 10) % 10];
}
/**
* 获取下一个地支
* @param {string} zhi - 当前地支
* @returns {string} 下一个地支
*/
getNextZhi(zhi) {
const zhis = ['子', '丑', '寅', '卯', '辰', '巳', '午', '未', '申', '酉', '戌', '亥'];
const index = zhis.indexOf(zhi);
return zhis[(index + 1) % 12];
}
/**
* 获取上一个地支
* @param {string} zhi - 当前地支
* @returns {string} 上一个地支
*/
getPrevZhi(zhi) {
const zhis = ['子', '丑', '寅', '卯', '辰', '巳', '午', '未', '申', '酉', '戌', '亥'];
const index = zhis.indexOf(zhi);
return zhis[(index - 1 + 12) % 12];
}
/**
* 获取年份对应的干支
* @param {number} year - 年份
* @returns {string} 干支
*/
getYearGanZhi(year) {
// 以1984年甲子年为基准
const baseYear = 1984;
const yearDiff = year - baseYear;
const gans = ['甲', '乙', '丙', '丁', '戊', '己', '庚', '辛', '壬', '癸'];
const zhis = ['子', '丑', '寅', '卯', '辰', '巳', '午', '未', '申', '酉', '戌', '亥'];
const ganIndex = (yearDiff % 10 + 10) % 10;
const zhiIndex = (yearDiff % 12 + 12) % 12;
return gans[ganIndex] + zhis[zhiIndex];
}
/**
* 获取贵人星
* @param {string} dayGan - 日干
* @param {Array} zhis - 地支数组
* @returns {Array} 贵人星
*/
getNobleStars(dayGan, zhis) {
const nobleStarMap = {
'甲': ['丑', '未'], '乙': ['子', '申'], '丙': ['亥', '酉'], '丁': ['亥', '酉'],
'戊': ['丑', '未'], '己': ['子', '申'], '庚': ['丑', '未'], '辛': ['午', '寅'],
'壬': ['卯', '巳'], '癸': ['卯', '巳']
};
const nobleZhis = nobleStarMap[dayGan] || [];
return zhis.filter(zhi => nobleZhis.includes(zhi));
}
/**
* 获取桃花星
* @param {string} dayZhi - 日支
* @param {Array} zhis - 地支数组
* @returns {Array} 桃花星
*/
getPeachBlossom(dayZhi, zhis) {
const peachBlossomMap = {
'子': '酉', '丑': '午', '寅': '卯', '卯': '子',
'辰': '酉', '巳': '午', '午': '卯', '未': '子',
'申': '酉', '酉': '午', '戌': '卯', '亥': '子'
};
const peachBlossom = peachBlossomMap[dayZhi];
return zhis.filter(zhi => zhi === peachBlossom);
}
/**
* 获取文昌星
* @param {string} dayGan - 日干
* @param {Object} fourPillars - 四柱
* @returns {Array} 文昌星
*/
getAcademicStars(dayGan, fourPillars) {
const academicStarMap = {
'甲': '巳', '乙': '午', '丙': '申', '丁': '酉',
'戊': '申', '己': '酉', '庚': '亥', '辛': '子',
'壬': '寅', '癸': '卯'
};
const academicStar = academicStarMap[dayGan];
const zhis = [fourPillars.year.earthly_branch, fourPillars.month.earthly_branch, fourPillars.day.earthly_branch, fourPillars.hour.earthly_branch];
return zhis.filter(zhi => zhi === academicStar);
}
/**
* 获取财星
* @param {string} dayGan - 日干
* @param {Object} fourPillars - 四柱
* @returns {Array} 财星
*/
getWealthStars(dayGan, fourPillars) {
// 简化实现,返回空数组
return [];
}
/**
* 获取权力星
* @param {string} dayGan - 日干
* @param {Object} fourPillars - 四柱
* @returns {Array} 权力星
*/
getPowerStars(dayGan, fourPillars) {
// 简化实现,返回空数组
return [];
}
/**
* 获取驿马星
* @param {string} dayZhi - 日支
* @param {Object} fourPillars - 四柱
* @returns {Array} 驿马星
*/
getTravelHorse(dayZhi, fourPillars) {
const travelHorseMap = {
'子': '寅', '丑': '亥', '寅': '申', '卯': '巳',
'辰': '寅', '巳': '亥', '午': '申', '未': '巳',
'申': '寅', '酉': '亥', '戌': '申', '亥': '巳'
};
const travelHorse = travelHorseMap[dayZhi];
const zhis = [fourPillars.year.earthly_branch, fourPillars.month.earthly_branch, fourPillars.day.earthly_branch, fourPillars.hour.earthly_branch];
return zhis.filter(zhi => zhi === travelHorse);
}
/**
* 获取灾煞星
* @param {string} dayGan - 日干
* @param {Object} fourPillars - 四柱
* @returns {Array} 灾煞星
*/
getDisasterStars(dayGan, fourPillars) {
// 简化实现,返回空数组
return [];
}
/**
* 获取地支对应的五行
* @param {string} zhi - 地支
* @returns {string} 五行
*/
getZhiElement(zhi) {
const zhiElements = {
'子': 'water', '丑': 'earth', '寅': 'wood', '卯': 'wood',
'辰': 'earth', '巳': 'fire', '午': 'fire', '未': 'earth',
'申': 'metal', '酉': 'metal', '戌': 'earth', '亥': 'water'
};
return zhiElements[zhi] || '未知';
}
/**
* 获取地支藏干
* @param {string} zhi - 地支
* @returns {Array} 藏干数组
*/
getZhiHiddenGans(zhi) {
const hiddenGans = {
'子': [{ gan: '癸', strength: 1.0 }],
'丑': [{ gan: '己', strength: 0.6 }, { gan: '癸', strength: 0.3 }, { gan: '辛', strength: 0.1 }],
'寅': [{ gan: '甲', strength: 0.6 }, { gan: '丙', strength: 0.3 }, { gan: '戊', strength: 0.1 }],
'卯': [{ gan: '乙', strength: 1.0 }],
'辰': [{ gan: '戊', strength: 0.6 }, { gan: '乙', strength: 0.3 }, { gan: '癸', strength: 0.1 }],
'巳': [{ gan: '丙', strength: 0.6 }, { gan: '戊', strength: 0.3 }, { gan: '庚', strength: 0.1 }],
'午': [{ gan: '丁', strength: 0.7 }, { gan: '己', strength: 0.3 }],
'未': [{ gan: '己', strength: 0.6 }, { gan: '丁', strength: 0.3 }, { gan: '乙', strength: 0.1 }],
'申': [{ gan: '庚', strength: 0.6 }, { gan: '壬', strength: 0.3 }, { gan: '戊', strength: 0.1 }],
'酉': [{ gan: '辛', strength: 1.0 }],
'戌': [{ gan: '戊', strength: 0.6 }, { gan: '辛', strength: 0.3 }, { gan: '丁', strength: 0.1 }],
'亥': [{ gan: '壬', strength: 0.7 }, { gan: '甲', strength: 0.3 }]
};
return hiddenGans[zhi] || [];
}
/**
* 分析五行平衡
* @param {Object} elements - 五行统计
* @param {string} dayMasterElement - 日主五行
* @returns {Object} 分析结果
*/
analyzeFiveElementBalance(elements, dayMasterElement) {
const total = Object.values(elements).reduce((sum, count) => sum + count, 0);
const dayMasterStrength = elements[dayMasterElement] / total;
let favorable_elements = [];
let unfavorable_elements = [];
if (dayMasterStrength < 0.2) {
// 日主偏弱,需要生扶
favorable_elements = this.getGeneratingElements(dayMasterElement);
unfavorable_elements = this.getWeakeningElements(dayMasterElement);
} else if (dayMasterStrength > 0.4) {
// 日主偏强,需要克泄
favorable_elements = this.getWeakeningElements(dayMasterElement);
unfavorable_elements = this.getGeneratingElements(dayMasterElement);
} else {
// 日主中和
favorable_elements = [dayMasterElement];
unfavorable_elements = [];
}
return {
balance_type: dayMasterStrength < 0.2 ? '偏弱' : dayMasterStrength > 0.4 ? '偏强' : '中和',
day_master_strength: (dayMasterStrength * 100).toFixed(1) + '%',
favorable_elements,
unfavorable_elements
};
}
/**
* 获取生扶元素
* @param {string} element - 五行
* @returns {Array} 生扶元素
*/
getGeneratingElements(element) {
const generating = {
'wood': ['water', 'wood'],
'fire': ['wood', 'fire'],
'earth': ['fire', 'earth'],
'metal': ['earth', 'metal'],
'water': ['metal', 'water']
};
return generating[element] || [];
}
/**
* 获取克泄元素
* @param {string} element - 五行
* @returns {Array} 克泄元素
*/
getWeakeningElements(element) {
const weakening = {
'wood': ['metal', 'fire'],
'fire': ['water', 'earth'],
'earth': ['wood', 'metal'],
'metal': ['fire', 'water'],
'water': ['earth', 'wood']
};
return weakening[element] || [];
}
/**
* 根据地支获取季节
* @param {string} zhi - 地支
* @returns {string} 季节
*/
getSeasonFromZhi(zhi) {
const seasons = {
'寅': '春', '卯': '春', '辰': '春',
'巳': '夏', '午': '夏', '未': '夏',
'申': '秋', '酉': '秋', '戌': '秋',
'亥': '冬', '子': '冬', '丑': '冬'
};
return seasons[zhi] || '未知';
}
/**
* 根据日干和时辰计算时干
* @param {string} dayStem - 日干
* @param {number} hour - 小时(0-23)
* @returns {string} 时干
*/
getHourGan(dayStem, hour) {
// 确定时辰索引
let hourIndex;
if (hour >= 23 || hour < 1) hourIndex = 0; // 子时
else if (hour >= 1 && hour < 3) hourIndex = 1; // 丑时
else if (hour >= 3 && hour < 5) hourIndex = 2; // 寅时
else if (hour >= 5 && hour < 7) hourIndex = 3; // 卯时
else if (hour >= 7 && hour < 9) hourIndex = 4; // 辰时
else if (hour >= 9 && hour < 11) hourIndex = 5; // 巳时
else if (hour >= 11 && hour < 13) hourIndex = 6; // 午时
else if (hour >= 13 && hour < 15) hourIndex = 7; // 未时
else if (hour >= 15 && hour < 17) hourIndex = 8; // 申时
else if (hour >= 17 && hour < 19) hourIndex = 9; // 酉时
else if (hour >= 19 && hour < 21) hourIndex = 10; // 戌时
else hourIndex = 11; // 亥时
// 根据日干推算时干
const dayStemIndex = this.heavenlyStems.indexOf(dayStem);
const hourStemIndex = (dayStemIndex * 2 + hourIndex) % 10;
return this.heavenlyStems[hourStemIndex];
}
/**
* 计算流年
* @param {Date} birthDate - 出生日期
* @returns {Array} 流年信息
*/
calculateAnnualLuck(birthDate) {
const currentYear = new Date().getFullYear();
const birthYear = birthDate.getFullYear();
const annualLuck = [];
// 计算近10年的流年
for (let i = -5; i <= 5; i++) {
const year = currentYear + i;
const age = year - birthYear;
// 计算该年的干支
const baseYear = 1984; // 甲子年
const yearOffset = (year - baseYear) % 60;
const stemIndex = yearOffset % 10;
const branchIndex = yearOffset % 12;
const stem = this.heavenlyStems[stemIndex < 0 ? stemIndex + 10 : stemIndex];
const branch = this.earthlyBranches[branchIndex < 0 ? branchIndex + 12 : branchIndex];
annualLuck.push({
year: year,
age: age,
stem: stem,
branch: branch,
element: this.stemElements[stem],
branch_element: this.branchElements[branch],
is_current: year === currentYear
});
}
return annualLuck;
}
/**
* 分析五行强弱
* @param {Array} pillars - 四柱
* @returns {Object} 五行分析
*/
analyzeElements(pillars) {
const elements = { '木': 0, '火': 0, '土': 0, '金': 0, '水': 0 };
// 统计天干地支五行
pillars.forEach(pillar => {
elements[pillar.element] += 2; // 天干权重2
elements[pillar.branch_element] += 1; // 地支权重1
});
// 找出最强和最弱的五行
const sortedElements = Object.entries(elements).sort((a, b) => b[1] - a[1]);
const strongest = sortedElements[0];
const weakest = sortedElements[sortedElements.length - 1];
// 计算日主强弱
const dayMasterElement = pillars[2].element; // 日柱天干
const dayMasterStrength = elements[dayMasterElement];
const totalStrength = Object.values(elements).reduce((sum, val) => sum + val, 0);
const strengthRatio = dayMasterStrength / totalStrength;
let dayMasterStatus;
if (strengthRatio > 0.4) dayMasterStatus = '偏强';
else if (strengthRatio > 0.25) dayMasterStatus = '中和';
else if (strengthRatio > 0.15) dayMasterStatus = '偏弱';
else dayMasterStatus = '极弱';
return {
element_count: elements,
strongest_element: { element: strongest[0], count: strongest[1] },
weakest_element: { element: weakest[0], count: weakest[1] },
day_master_element: dayMasterElement,
day_master_strength: dayMasterStrength,
day_master_status: dayMasterStatus,
strength_ratio: strengthRatio,
total_strength: totalStrength
};
}
/**
* 分析十神关系
* @param {string} dayMasterStem - 日主天干
* @param {Array} pillars - 四柱
* @returns {Object} 十神分析
*/
analyzeTenGods(dayMasterStem, pillars) {
const dayMasterElement = this.stemElements[dayMasterStem];
const dayMasterPolarity = this.stemPolarity[dayMasterStem];
const tenGodsCount = {};
const tenGodsPositions = [];
pillars.forEach((pillar, index) => {
const positions = ['年', '月', '日', '时'];
// 分析天干十神
const stemTenGod = this.getStemTenGod(dayMasterStem, pillar.stem);
if (stemTenGod) {
tenGodsCount[stemTenGod] = (tenGodsCount[stemTenGod] || 0) + 1;
tenGodsPositions.push({
position: positions[index],
type: '天干',
stem: pillar.stem,
ten_god: stemTenGod
});
}
// 分析地支藏干十神(简化处理)
const branchHiddenStems = this.getBranchHiddenStems(pillar.branch);
branchHiddenStems.forEach(hiddenStem => {
const hiddenTenGod = this.getStemTenGod(dayMasterStem, hiddenStem.stem);
if (hiddenTenGod) {
tenGodsCount[hiddenTenGod] = (tenGodsCount[hiddenTenGod] || 0) + hiddenStem.strength;
tenGodsPositions.push({
position: positions[index],
type: '地支藏干',
branch: pillar.branch,
hidden_stem: hiddenStem.stem,
ten_god: hiddenTenGod,
strength: hiddenStem.strength
});
}
});
});
return {
day_master: {
stem: dayMasterStem,
element: dayMasterElement,
polarity: dayMasterPolarity
},
ten_gods_count: tenGodsCount,
ten_gods_positions: tenGodsPositions
};
}
/**
* 分析格局
* @param {Array} pillars - 四柱
* @param {Object} elementAnalysis - 五行分析
* @returns {Object} 格局分析
*/
analyzePattern(pillars, elementAnalysis) {
const dayMaster = pillars[2]; // 日柱
const monthBranch = pillars[1].branch; // 月支
// 判断是否为特殊格局
const specialPattern = this.identifySpecialPattern(pillars, elementAnalysis);
if (specialPattern) {
return {
type: '特殊格局',
pattern: specialPattern,
description: this.getPatternDescription(specialPattern)
};
}
// 判断普通格局(以月支为准)
const monthElement = this.branchElements[monthBranch];
const dayMasterElement = dayMaster.element;
let pattern;
if (monthElement === dayMasterElement) {
pattern = '建禄格';
} else if (this.isElementGenerating(monthElement, dayMasterElement)) {
pattern = '印绶格';
} else if (this.isElementGenerating(dayMasterElement, monthElement)) {
pattern = '食伤格';
} else if (this.isElementDestructive(dayMasterElement, monthElement)) {
pattern = '财格';
} else if (this.isElementDestructive(monthElement, dayMasterElement)) {
pattern = '官杀格';
} else {
pattern = '普通格局';
}
return {
type: '普通格局',
pattern: pattern,
month_element: monthElement,
day_master_element: dayMasterElement,
description: this.getPatternDescription(pattern)
};
}
// 辅助方法
/**
* 获取立春时间
* @param {number} year - 年份
* @returns {Date} 立春时间
*/
getSpringStart(year) {
// 简化计算,实际应该使用精确的节气计算
return new Date(year, 1, 4); // 大约2月4日
}
/**
* 获取节气月份
* @param {number} month - 月份
* @param {number} day - 日期
* @returns {number} 节气月份
*/
getSolarMonth(month, day) {
// 简化处理,实际应该根据精确节气计算
const solarMonthStart = [
[2, 4], // 立春
[3, 6], // 惊蛰
[4, 5], // 清明
[5, 6], // 立夏
[6, 6], // 芒种
[7, 7], // 小暑
[8, 8], // 立秋
[9, 8], // 白露
[10, 8], // 寒露
[11, 8], // 立冬
[12, 7], // 大雪
[1, 6] // 小寒
];
for (let i = 0; i < solarMonthStart.length; i++) {
const [startMonth, startDay] = solarMonthStart[i];
if (month === startMonth && day >= startDay) {
return (i + 1) % 12 || 12;
}
}
return month;
}
/**
* 获取儒略日
* @param {Date} date - 日期
* @returns {number} 儒略日
*/
getJulianDay(date) {
const year = date.getFullYear();
const month = date.getMonth() + 1;
const day = date.getDate();
let a = Math.floor((14 - month) / 12);
let y = year + 4800 - a;
let m = month + 12 * a - 3;
return day + Math.floor((153 * m + 2) / 5) + 365 * y + Math.floor(y / 4) - Math.floor(y / 100) + Math.floor(y / 400) - 32045;
}
/**
* 获取时辰范围
* @param {number} hourIndex - 时辰索引
* @returns {string} 时辰范围
*/
getHourRange(hourIndex) {
const ranges = [
'23:00-01:00', '01:00-03:00', '03:00-05:00', '05:00-07:00',
'07:00-09:00', '09:00-11:00', '11:00-13:00', '13:00-15:00',
'15:00-17:00', '17:00-19:00', '19:00-21:00', '21:00-23:00'
];
return ranges[hourIndex];
}
/**
* 计算起运年龄
* @param {Date} birthDate - 出生日期
* @param {Object} monthPillar - 月柱
* @param {boolean} isForward - 是否顺行
* @returns {number} 起运年龄
*/
calculateLuckStartAge(birthDate, monthPillar, isForward) {
// 简化计算,实际应该根据节气精确计算
return Math.floor(Math.random() * 8) + 1; // 1-8岁之间
}
/**
* 获取天干十神
* @param {string} dayMaster - 日主
* @param {string} targetStem - 目标天干
* @returns {string} 十神
*/
getStemTenGod(dayMaster, targetStem) {
if (dayMaster === targetStem) return null; // 日主本身
const dayElement = this.stemElements[dayMaster];
const dayPolarity = this.stemPolarity[dayMaster];
const targetElement = this.stemElements[targetStem];
const targetPolarity = this.stemPolarity[targetStem];
// 根据五行关系和阴阳确定十神
if (dayElement === targetElement) {
return dayPolarity === targetPolarity ? '比肩' : '劫财';
} else if (this.isElementGenerating(dayElement, targetElement)) {
return dayPolarity === targetPolarity ? '食神' : '伤官';
} else if (this.isElementDestructive(dayElement, targetElement)) {
return dayPolarity === targetPolarity ? '偏财' : '正财';
} else if (this.isElementDestructive(targetElement, dayElement)) {
return dayPolarity === targetPolarity ? '七杀' : '正官';
} else if (this.isElementGenerating(targetElement, dayElement)) {
return dayPolarity === targetPolarity ? '偏印' : '正印';
}
return '未知';
}
/**
* 获取十神信息
* @param {string} god - 十神名称
* @returns {Object|null} 十神信息
*/
getTenGod(god) {
return this.baziDB.getTenGod(god);
}
/**
* 统计十神分布
* @param {Object} tenGods - 十神数据
* @returns {Object} 十神分布统计
*/
getTenGodDistribution(tenGods) {
const distribution = {
'比肩': 0, '劫财': 0, '食神': 0, '伤官': 0, '偏财': 0,
'正财': 0, '七杀': 0, '正官': 0, '偏印': 0, '正印': 0
};
Object.values(tenGods).forEach(pillar => {
if (pillar.gan_ten_god) {
distribution[pillar.gan_ten_god] = (distribution[pillar.gan_ten_god] || 0) + 1;
}
if (pillar.zhi_ten_god) {
distribution[pillar.zhi_ten_god] = (distribution[pillar.zhi_ten_god] || 0) + 0.5;
}
});
return distribution;
}
/**
* 分析十神特点
* @param {Object} distribution - 十神分布
* @param {string} dayGan - 日干
* @returns {Object} 十神特点分析
*/
analyzeTenGodCharacteristics(distribution, dayGan) {
const characteristics = {
dominant_gods: [],
personality_traits: [],
career_tendencies: [],
wealth_potential: '中等',
relationship_style: '平和'
};
// 找出主要十神
Object.entries(distribution).forEach(([god, count]) => {
if (count >= 1) {
characteristics.dominant_gods.push(god);
}
});
// 根据主要十神分析特点
if (distribution['正官'] > 0 || distribution['七杀'] > 0) {
characteristics.personality_traits.push('有责任心', '领导能力强');
characteristics.career_tendencies.push('管理', '公务员');
}
if (distribution['正财'] > 0 || distribution['偏财'] > 0) {
characteristics.wealth_potential = '较强';
characteristics.career_tendencies.push('商业', '金融');
}
if (distribution['食神'] > 0 || distribution['伤官'] > 0) {
characteristics.personality_traits.push('有创意', '表达能力强');
characteristics.career_tendencies.push('艺术', '创意');
}
return characteristics;
}
/**
* 获取年份干支
* @param {number} year - 年份
* @returns {string} 年干支
*/
getYearGanZhi(year) {
// 甲子年为1984年,每60年一个循环
const baseYear = 1984;
const yearOffset = (year - baseYear) % 60;
const stemIndex = yearOffset % 10;
const branchIndex = yearOffset % 12;
const stem = this.heavenlyStems[stemIndex < 0 ? stemIndex + 10 : stemIndex];
const branch = this.earthlyBranches[branchIndex < 0 ? branchIndex + 12 : branchIndex];
return stem + branch;
}
/**
* 获取月份干支
* @param {number} year - 年份
* @param {number} month - 月份(1-12)
* @returns {string} 月干支
*/
getMonthGanZhi(year, month) {
// 月支固定:寅月(立春)、卯月(惊蛰)、辰月(清明)...
const monthBranches = ['寅', '卯', '辰', '巳', '午', '未', '申', '酉', '戌', '亥', '子', '丑'];
const branchIndex = (month - 1) % 12;
const branch = monthBranches[branchIndex];
// 月干根据年干推算
const yearGanZhi = this.getYearGanZhi(year);
const yearStem = yearGanZhi.charAt(0);
const yearStemIndex = this.heavenlyStems.indexOf(yearStem);
const monthStemIndex = (yearStemIndex * 2 + month - 1) % 10;
const stem = this.heavenlyStems[monthStemIndex];
return stem + branch;
}
/**
* 获取地支主气天干
* @param {string} branch - 地支
* @returns {string} 主气天干
*/
getZhiMainGan(branch) {
const hiddenStems = this.getBranchHiddenStems(branch);
return hiddenStems.length > 0 ? hiddenStems[0].stem : null;
}
/**
* 获取地支藏干
* @param {string} branch - 地支
* @returns {Array} 藏干数组
*/
getBranchHiddenStems(branch) {
const hiddenStems = {
'子': [{ stem: '癸', strength: 1.0 }],
'丑': [{ stem: '己', strength: 0.6 }, { stem: '癸', strength: 0.3 }, { stem: '辛', strength: 0.1 }],
'寅': [{ stem: '甲', strength: 0.6 }, { stem: '丙', strength: 0.3 }, { stem: '戊', strength: 0.1 }],
'卯': [{ stem: '乙', strength: 1.0 }],
'辰': [{ stem: '戊', strength: 0.6 }, { stem: '乙', strength: 0.3 }, { stem: '癸', strength: 0.1 }],
'巳': [{ stem: '丙', strength: 0.6 }, { stem: '戊', strength: 0.3 }, { stem: '庚', strength: 0.1 }],
'午': [{ stem: '丁', strength: 0.7 }, { stem: '己', strength: 0.3 }],
'未': [{ stem: '己', strength: 0.6 }, { stem: '丁', strength: 0.3 }, { stem: '乙', strength: 0.1 }],
'申': [{ stem: '庚', strength: 0.6 }, { stem: '壬', strength: 0.3 }, { stem: '戊', strength: 0.1 }],
'酉': [{ stem: '辛', strength: 1.0 }],
'戌': [{ stem: '戊', strength: 0.6 }, { stem: '辛', strength: 0.3 }, { stem: '丁', strength: 0.1 }],
'亥': [{ stem: '壬', strength: 0.7 }, { stem: '甲', strength: 0.3 }]
};
return hiddenStems[branch] || [];
}
/**
* 判断五行相生关系
* @param {string} element1 - 五行1
* @param {string} element2 - 五行2
* @returns {boolean} 是否相生
*/
isElementGenerating(element1, element2) {
const generatingCycles = {
'木': '火',
'火': '土',
'土': '金',
'金': '水',
'水': '木'
};
return generatingCycles[element1] === element2;
}
/**
* 判断五行相克关系
* @param {string} element1 - 五行1
* @param {string} element2 - 五行2
* @returns {boolean} 是否相克
*/
isElementDestructive(element1, element2) {
const destructiveCycles = {
'木': '土',
'火': '金',
'土': '水',
'金': '木',
'水': '火'
};
return destructiveCycles[element1] === element2;
}
/**
* 识别特殊格局
* @param {Array} pillars - 四柱
* @param {Object} elementAnalysis - 五行分析
* @returns {string|null} 特殊格局名称
*/
identifySpecialPattern(pillars, elementAnalysis) {
// 从革格(金旺)
if (elementAnalysis.strongest_element.element === '金' &&
elementAnalysis.strongest_element.count >= 8) {
return '从革格';
}
// 炎上格(火旺)
if (elementAnalysis.strongest_element.element === '火' &&
elementAnalysis.strongest_element.count >= 8) {
return '炎上格';
}
// 润下格(水旺)
if (elementAnalysis.strongest_element.element === '水' &&
elementAnalysis.strongest_element.count >= 8) {
return '润下格';
}
// 曲直格(木旺)
if (elementAnalysis.strongest_element.element === '木' &&
elementAnalysis.strongest_element.count >= 8) {
return '曲直格';
}
// 稼穑格(土旺)
if (elementAnalysis.strongest_element.element === '土' &&
elementAnalysis.strongest_element.count >= 8) {
return '稼穑格';
}
return null;
}
/**
* 获取格局描述
* @param {string} pattern - 格局名称
* @returns {string} 格局描述
*/
getPatternDescription(pattern) {
const descriptions = {
'建禄格': '日主与月支同气,身强有力,适合创业发展',
'印绶格': '月支生助日主,聪明好学,适合文职工作',
'食伤格': '日主生月支,才华横溢,适合创意工作',
'财格': '日主克月支,善于理财,适合商业发展',
'官杀格': '月支克日主,有责任心,适合管理工作',
'从革格': '金气极旺,性格刚强,适合技术工作',
'炎上格': '火气极旺,热情积极,适合表演艺术',
'润下格': '水气极旺,智慧灵活,适合流动性工作',
'曲直格': '木气极旺,仁慈正直,适合教育工作',
'稼穑格': '土气极旺,诚实稳重,适合农业地产'
};
return descriptions[pattern] || '格局特殊,需要综合分析';
}
/**
* 验证八字数据
* @param {Object} bazi - 八字数据
* @returns {boolean} 是否有效
*/
validateBazi(bazi) {
if (!bazi || typeof bazi !== 'object') {
return false;
}
// 检查必要字段
const requiredFields = ['birth_info', 'four_pillars', 'element_analysis'];
for (const field of requiredFields) {
if (!bazi[field]) {
return false;
}
}
// 检查四柱
const pillars = ['year', 'month', 'day', 'hour'];
for (const pillar of pillars) {
if (!bazi.four_pillars[pillar] ||
!bazi.four_pillars[pillar].stem ||
!bazi.four_pillars[pillar].branch) {
return false;
}
}
return true;
}
}
module.exports = BaziCalculator;