bazi.js 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245
  1. /**
  2. * 八字计算工具类
  3. * 基于传统命理学理论实现八字计算、分析和配对功能
  4. * 支持专业API验证和本地算法双重计算
  5. */
  6. import { BAZI_API_CONFIG } from '@/config/api-config.js'
  7. // 天干
  8. const TIANGAN = ['甲', '乙', '丙', '丁', '戊', '己', '庚', '辛', '壬', '癸']
  9. // 地支
  10. const DIZHI = ['子', '丑', '寅', '卯', '辰', '巳', '午', '未', '申', '酉', '戌', '亥']
  11. // 天干五行
  12. const TIANGAN_WUXING = {
  13. '甲': '木', '乙': '木',
  14. '丙': '火', '丁': '火',
  15. '戊': '土', '己': '土',
  16. '庚': '金', '辛': '金',
  17. '壬': '水', '癸': '水'
  18. }
  19. // 地支五行
  20. const DIZHI_WUXING = {
  21. '子': '水', '丑': '土', '寅': '木', '卯': '木',
  22. '辰': '土', '巳': '火', '午': '火', '未': '土',
  23. '申': '金', '酉': '金', '戌': '土', '亥': '水'
  24. }
  25. // 天干阴阳
  26. const TIANGAN_YINYANG = {
  27. '甲': '阳', '乙': '阴', '丙': '阳', '丁': '阴', '戊': '阳',
  28. '己': '阴', '庚': '阳', '辛': '阴', '壬': '阳', '癸': '阴'
  29. }
  30. // 地支阴阳
  31. const DIZHI_YINYANG = {
  32. '子': '阳', '丑': '阴', '寅': '阳', '卯': '阴', '辰': '阳', '巳': '阴',
  33. '午': '阳', '未': '阴', '申': '阳', '酉': '阴', '戌': '阳', '亥': '阴'
  34. }
  35. // 十二生肖
  36. const SHENGXIAO = ['鼠', '牛', '虎', '兔', '龙', '蛇', '马', '羊', '猴', '鸡', '狗', '猪']
  37. // 月份地支对照表(节气月)
  38. const MONTH_DIZHI = {
  39. 2: '寅', 3: '卯', 4: '辰', 5: '巳', 6: '午', 7: '未',
  40. 8: '申', 9: '酉', 10: '戌', 11: '亥', 12: '子', 1: '丑'
  41. }
  42. // 时辰对照表
  43. const TIME_DIZHI = {
  44. 23: '子', 0: '子', 1: '丑', 2: '丑', 3: '寅', 4: '寅',
  45. 5: '卯', 6: '卯', 7: '辰', 8: '辰', 9: '巳', 10: '巳',
  46. 11: '午', 12: '午', 13: '未', 14: '未', 15: '申', 16: '申',
  47. 17: '酉', 18: '酉', 19: '戌', 20: '戌', 21: '亥', 22: '亥'
  48. }
  49. // 五行生克关系
  50. const WUXING_RELATION = {
  51. '生': {
  52. '木': '火', '火': '土', '土': '金', '金': '水', '水': '木'
  53. },
  54. '克': {
  55. '木': '土', '火': '金', '土': '水', '金': '木', '水': '火'
  56. }
  57. }
  58. // 纳音表(简化版)
  59. const NAYIN = {
  60. '甲子': '海中金', '乙丑': '海中金', '丙寅': '炉中火', '丁卯': '炉中火',
  61. '戊辰': '大林木', '己巳': '大林木', '庚午': '路旁土', '辛未': '路旁土',
  62. '壬申': '剑锋金', '癸酉': '剑锋金', '甲戌': '山头火', '乙亥': '山头火',
  63. '丙子': '涧下水', '丁丑': '涧下水', '戊寅': '城头土', '己卯': '城头土',
  64. '庚辰': '白蜡金', '辛巳': '白蜡金', '壬午': '杨柳木', '癸未': '杨柳木',
  65. '甲申': '泉中水', '乙酉': '泉中水', '丙戌': '屋上土', '丁亥': '屋上土',
  66. '戊子': '霹雳火', '己丑': '霹雳火', '庚寅': '松柏木', '辛卯': '松柏木',
  67. '壬辰': '长流水', '癸巳': '长流水', '甲午': '砂中金', '乙未': '砂中金',
  68. '丙申': '山下火', '丁酉': '山下火', '戊戌': '平地木', '己亥': '平地木',
  69. '庚子': '壁上土', '辛丑': '壁上土', '壬寅': '金箔金', '癸卯': '金箔金',
  70. '甲辰': '佛灯火', '乙巳': '佛灯火', '丙午': '天河水', '丁未': '天河水',
  71. '戊申': '大驿土', '己酉': '大驿土', '庚戌': '钗钏金', '辛亥': '钗钏金',
  72. '壬子': '桑柘木', '癸丑': '桑柘木', '甲寅': '大溪水', '乙卯': '大溪水',
  73. '丙辰': '砂中土', '丁巳': '砂中土', '戊午': '天上火', '己未': '天上火',
  74. '庚申': '石榴木', '辛酉': '石榴木', '壬戌': '大海水', '癸亥': '大海水'
  75. }
  76. /**
  77. * 计算八字
  78. * @param {Date} birthDate 出生日期
  79. * @param {Number} hour 出生时辰(0-23)
  80. * @returns {Object} 八字信息
  81. */
  82. export function calculateBaZi(birthDate, hour) {
  83. const year = birthDate.getFullYear()
  84. const month = birthDate.getMonth() + 1
  85. const day = birthDate.getDate()
  86. // 计算年柱
  87. const yearGanZhi = getYearGanZhi(year)
  88. // 计算月柱
  89. const monthGanZhi = getMonthGanZhi(year, month)
  90. // 计算日柱
  91. const dayGanZhi = getDayGanZhi(year, month, day)
  92. // 计算时柱
  93. const hourGanZhi = getHourGanZhi(dayGanZhi.gan, hour)
  94. // 分析八字
  95. const analysis = analyzeBaZi({
  96. year: yearGanZhi,
  97. month: monthGanZhi,
  98. day: dayGanZhi,
  99. hour: hourGanZhi
  100. })
  101. return {
  102. year: yearGanZhi,
  103. month: monthGanZhi,
  104. day: dayGanZhi,
  105. hour: hourGanZhi,
  106. birthInfo: {
  107. year: year,
  108. month: month,
  109. day: day,
  110. hour: hour,
  111. shengxiao: SHENGXIAO[(year - 4) % 12],
  112. nayin: NAYIN[yearGanZhi.ganZhi] || '未知'
  113. },
  114. analysis: analysis,
  115. baziString: `${yearGanZhi.ganZhi} ${monthGanZhi.ganZhi} ${dayGanZhi.ganZhi} ${hourGanZhi.ganZhi}`,
  116. rizhu: dayGanZhi.ganZhi,
  117. riganWuxing: TIANGAN_WUXING[dayGanZhi.gan]
  118. }
  119. }
  120. /**
  121. * 获取年柱干支
  122. */
  123. function getYearGanZhi(year) {
  124. // 以1900年庚子年为基准计算
  125. const baseYear = 1900
  126. const yearOffset = year - baseYear
  127. const ganIndex = (yearOffset + 6) % 10 // 庚为第7个天干,索引为6
  128. const zhiIndex = (yearOffset + 0) % 12 // 子为第1个地支,索引为0
  129. const gan = TIANGAN[ganIndex]
  130. const zhi = DIZHI[zhiIndex]
  131. return {
  132. gan: gan,
  133. zhi: zhi,
  134. ganZhi: gan + zhi,
  135. wuxing: TIANGAN_WUXING[gan],
  136. yinyang: TIANGAN_YINYANG[gan]
  137. }
  138. }
  139. /**
  140. * 获取月柱干支
  141. */
  142. function getMonthGanZhi(year, month) {
  143. // 简化计算,实际应该根据节气
  144. const zhi = MONTH_DIZHI[month]
  145. // 月干计算:年干为甲己配丙寅,乙庚丁卯始...
  146. const yearGan = getYearGanZhi(year).gan
  147. let ganIndex = 0
  148. switch (yearGan) {
  149. case '甲': case '己': ganIndex = 2; break // 丙
  150. case '乙': case '庚': ganIndex = 3; break // 丁
  151. case '丙': case '辛': ganIndex = 4; break // 戊
  152. case '丁': case '壬': ganIndex = 5; break // 己
  153. case '戊': case '癸': ganIndex = 6; break // 庚
  154. }
  155. // 从寅月开始计算
  156. const monthOffset = month === 1 ? 11 : (month === 2 ? 0 : month - 2)
  157. ganIndex = (ganIndex + monthOffset) % 10
  158. const gan = TIANGAN[ganIndex]
  159. return {
  160. gan: gan,
  161. zhi: zhi,
  162. ganZhi: gan + zhi,
  163. wuxing: TIANGAN_WUXING[gan],
  164. yinyang: TIANGAN_YINYANG[gan]
  165. }
  166. }
  167. /**
  168. * 获取日柱干支(简化算法)
  169. */
  170. function getDayGanZhi(year, month, day) {
  171. // 以公元1年1月1日为甲子日计算(简化)
  172. const date = new Date(year, month - 1, day)
  173. const daysSince1900 = Math.floor((date - new Date(1900, 0, 1)) / (24 * 60 * 60 * 1000))
  174. // 1900年1月1日为庚戌日
  175. const ganIndex = (daysSince1900 + 6) % 10 // 庚的索引是6
  176. const zhiIndex = (daysSince1900 + 10) % 12 // 戌的索引是10
  177. const gan = TIANGAN[ganIndex]
  178. const zhi = DIZHI[zhiIndex]
  179. return {
  180. gan: gan,
  181. zhi: zhi,
  182. ganZhi: gan + zhi,
  183. wuxing: TIANGAN_WUXING[gan],
  184. yinyang: TIANGAN_YINYANG[gan]
  185. }
  186. }
  187. /**
  188. * 获取时柱干支
  189. */
  190. function getHourGanZhi(dayGan, hour) {
  191. const zhi = TIME_DIZHI[hour] || '未知'
  192. // 时干计算:甲己还是甲,乙庚丙作初...
  193. let ganIndex = 0
  194. const zhiIndex = DIZHI.indexOf(zhi)
  195. switch (dayGan) {
  196. case '甲': case '己': ganIndex = 0; break // 甲
  197. case '乙': case '庚': ganIndex = 2; break // 丙
  198. case '丙': case '辛': ganIndex = 4; break // 戊
  199. case '丁': case '壬': ganIndex = 6; break // 庚
  200. case '戊': case '癸': ganIndex = 8; break // 壬
  201. }
  202. ganIndex = (ganIndex + zhiIndex) % 10
  203. const gan = TIANGAN[ganIndex]
  204. return {
  205. gan: gan,
  206. zhi: zhi,
  207. ganZhi: gan + zhi,
  208. wuxing: TIANGAN_WUXING[gan],
  209. yinyang: TIANGAN_YINYANG[gan]
  210. }
  211. }
  212. /**
  213. * 分析八字
  214. */
  215. function analyzeBaZi(bazi) {
  216. const wuxingCount = countWuXing(bazi)
  217. const qiangRuo = analyzeQiangRuo(bazi, wuxingCount)
  218. const yongshen = getYongShen(bazi, wuxingCount, qiangRuo)
  219. const personality = getPersonality(bazi)
  220. return {
  221. wuxingCount: wuxingCount,
  222. qiangRuo: qiangRuo,
  223. yongshen: yongshen,
  224. personality: personality,
  225. summary: generateBaziSummary(bazi, wuxingCount, qiangRuo, yongshen)
  226. }
  227. }
  228. /**
  229. * 统计五行个数
  230. */
  231. function countWuXing(bazi) {
  232. const count = { '金': 0, '木': 0, '水': 0, '火': 0, '土': 0 }
  233. // 统计天干
  234. Object.values(bazi).forEach(pillar => {
  235. const ganWuxing = TIANGAN_WUXING[pillar.gan]
  236. const zhiWuxing = DIZHI_WUXING[pillar.zhi]
  237. if (ganWuxing) count[ganWuxing]++
  238. if (zhiWuxing) count[zhiWuxing]++
  239. })
  240. return count
  241. }
  242. /**
  243. * 分析日主强弱
  244. */
  245. function analyzeQiangRuo(bazi, wuxingCount) {
  246. const riganWuxing = TIANGAN_WUXING[bazi.day.gan]
  247. const riganCount = wuxingCount[riganWuxing]
  248. // 简化分析:看日干五行在八字中的个数
  249. if (riganCount >= 3) {
  250. return { type: '身旺', description: '日主偏强,需要消耗和克制' }
  251. } else if (riganCount <= 1) {
  252. return { type: '身弱', description: '日主偏弱,需要生助和帮扶' }
  253. } else {
  254. return { type: '中和', description: '日主适中,平衡发展' }
  255. }
  256. }
  257. /**
  258. * 取用神
  259. */
  260. function getYongShen(bazi, wuxingCount, qiangRuo) {
  261. const riganWuxing = TIANGAN_WUXING[bazi.day.gan]
  262. if (qiangRuo.type === '身旺') {
  263. // 身旺用克泄耗
  264. const keWuxing = Object.keys(WUXING_RELATION.克).find(key => WUXING_RELATION.克[key] === riganWuxing)
  265. const xieWuxing = WUXING_RELATION.生[riganWuxing]
  266. return {
  267. primary: keWuxing || xieWuxing,
  268. description: `日主${riganWuxing}偏旺,宜用${keWuxing || xieWuxing}调候`
  269. }
  270. } else if (qiangRuo.type === '身弱') {
  271. // 身弱用生扶
  272. const shengWuxing = Object.keys(WUXING_RELATION.生).find(key => WUXING_RELATION.生[key] === riganWuxing)
  273. return {
  274. primary: shengWuxing || riganWuxing,
  275. description: `日主${riganWuxing}偏弱,宜用${shengWuxing || riganWuxing}生扶`
  276. }
  277. } else {
  278. return {
  279. primary: riganWuxing,
  280. description: `日主${riganWuxing}中和,保持平衡即可`
  281. }
  282. }
  283. }
  284. /**
  285. * 获取性格特征
  286. */
  287. function getPersonality(bazi) {
  288. const traits = []
  289. // 根据日干分析性格
  290. const rigan = bazi.day.gan
  291. switch (rigan) {
  292. case '甲':
  293. traits.push('正直', '有领导力', '创新精神')
  294. break
  295. case '乙':
  296. traits.push('温和', '适应力强', '善于变通')
  297. break
  298. case '丙':
  299. traits.push('热情', '积极向上', '善于表达')
  300. break
  301. case '丁':
  302. traits.push('细心', '有艺术天赋', '内心温暖')
  303. break
  304. case '戊':
  305. traits.push('踏实', '稳重可靠', '包容心强')
  306. break
  307. case '己':
  308. traits.push('务实', '善于理财', '注重细节')
  309. break
  310. case '庚':
  311. traits.push('果断', '意志坚强', '正义感强')
  312. break
  313. case '辛':
  314. traits.push('细腻', '品味高雅', '追求完美')
  315. break
  316. case '壬':
  317. traits.push('聪明', '适应力强', '善于沟通')
  318. break
  319. case '癸':
  320. traits.push('智慧', '直觉敏锐', '内心丰富')
  321. break
  322. }
  323. return traits
  324. }
  325. /**
  326. * 生成八字总结
  327. */
  328. function generateBaziSummary(bazi, wuxingCount, qiangRuo, yongshen) {
  329. const riganWuxing = TIANGAN_WUXING[bazi.day.gan]
  330. const maxWuxing = Object.keys(wuxingCount).reduce((a, b) => wuxingCount[a] > wuxingCount[b] ? a : b)
  331. const minWuxing = Object.keys(wuxingCount).reduce((a, b) => wuxingCount[a] < wuxingCount[b] ? a : b)
  332. return `您的八字为${bazi.baziString},日主${bazi.day.gan}${riganWuxing},${qiangRuo.description}。` +
  333. `八字中${maxWuxing}较旺,${minWuxing}较弱,${yongshen.description}。` +
  334. `建议在生活中多接触${yongshen.primary}属性的事物,有利于运势提升。`
  335. }
  336. /**
  337. * 八字配对分析
  338. * @param {Object} bazi1 第一个人的八字
  339. * @param {Object} bazi2 第二个人的八字
  340. * @returns {Object} 配对结果
  341. */
  342. export function analyzeBaziMatch(bazi1, bazi2) {
  343. // 日干配对
  344. const riganMatch = analyzeRiganMatch(bazi1.day.gan, bazi2.day.gan)
  345. // 五行配对
  346. const wuxingMatch = analyzeWuxingMatch(bazi1.riganWuxing, bazi2.riganWuxing)
  347. // 纳音配对
  348. const nayinMatch = analyzeNayinMatch(bazi1.birthInfo.nayin, bazi2.birthInfo.nayin)
  349. // 生肖配对
  350. const shengxiaoMatch = analyzeShengxiaoMatch(bazi1.birthInfo.shengxiao, bazi2.birthInfo.shengxiao)
  351. // 综合评分
  352. const totalScore = calculateMatchScore(riganMatch, wuxingMatch, nayinMatch, shengxiaoMatch)
  353. // 获取配对等级
  354. const level = getMatchLevel(totalScore)
  355. return {
  356. totalScore: totalScore,
  357. level: level.name,
  358. levelColor: level.color,
  359. riganMatch: riganMatch,
  360. wuxingMatch: wuxingMatch,
  361. nayinMatch: nayinMatch,
  362. shengxiaoMatch: shengxiaoMatch,
  363. advantages: generateMatchAdvantages(bazi1, bazi2),
  364. challenges: generateMatchChallenges(bazi1, bazi2),
  365. suggestions: generateMatchSuggestions(bazi1, bazi2, totalScore),
  366. summary: generateMatchSummary(bazi1, bazi2, totalScore)
  367. }
  368. }
  369. /**
  370. * 分析日干配对
  371. */
  372. function analyzeRiganMatch(gan1, gan2) {
  373. const wuxing1 = TIANGAN_WUXING[gan1]
  374. const wuxing2 = TIANGAN_WUXING[gan2]
  375. const yinyang1 = TIANGAN_YINYANG[gan1]
  376. const yinyang2 = TIANGAN_YINYANG[gan2]
  377. let score = 50
  378. let description = ''
  379. // 阴阳配合
  380. if (yinyang1 !== yinyang2) {
  381. score += 15
  382. description += '阴阳互补,'
  383. }
  384. // 五行关系
  385. if (WUXING_RELATION.生[wuxing1] === wuxing2) {
  386. score += 20
  387. description += `${gan1}生${gan2},相生和谐`
  388. } else if (WUXING_RELATION.生[wuxing2] === wuxing1) {
  389. score += 20
  390. description += `${gan2}生${gan1},相生和谐`
  391. } else if (WUXING_RELATION.克[wuxing1] === wuxing2) {
  392. score -= 10
  393. description += `${gan1}克${gan2},需要包容`
  394. } else if (WUXING_RELATION.克[wuxing2] === wuxing1) {
  395. score -= 10
  396. description += `${gan2}克${gan1},需要理解`
  397. } else if (wuxing1 === wuxing2) {
  398. score += 10
  399. description += '同气相求,志趣相投'
  400. } else {
  401. description += '五行中和,平稳发展'
  402. }
  403. return {
  404. score: Math.max(0, Math.min(100, score)),
  405. description: description
  406. }
  407. }
  408. /**
  409. * 分析五行配对
  410. */
  411. function analyzeWuxingMatch(wuxing1, wuxing2) {
  412. let score = 60
  413. let description = ''
  414. if (wuxing1 === wuxing2) {
  415. score = 75
  416. description = `同属${wuxing1},价值观相近,容易理解对方`
  417. } else if (WUXING_RELATION.生[wuxing1] === wuxing2 || WUXING_RELATION.生[wuxing2] === wuxing1) {
  418. score = 85
  419. description = `${wuxing1}与${wuxing2}相生,互相促进,相得益彰`
  420. } else if (WUXING_RELATION.克[wuxing1] === wuxing2 || WUXING_RELATION.克[wuxing2] === wuxing1) {
  421. score = 40
  422. description = `${wuxing1}与${wuxing2}相克,需要更多理解和包容`
  423. } else {
  424. score = 65
  425. description = `${wuxing1}与${wuxing2}中性关系,互补性较好`
  426. }
  427. return {
  428. score: score,
  429. description: description
  430. }
  431. }
  432. /**
  433. * 分析纳音配对
  434. */
  435. function analyzeNayinMatch(nayin1, nayin2) {
  436. // 简化的纳音配对逻辑
  437. let score = 60
  438. let description = ''
  439. if (nayin1 === nayin2) {
  440. score = 70
  441. description = `同为${nayin1},命理相同,默契度高`
  442. } else {
  443. // 根据纳音五行分析
  444. const nayin1Wuxing = getNayinWuxing(nayin1)
  445. const nayin2Wuxing = getNayinWuxing(nayin2)
  446. if (nayin1Wuxing && nayin2Wuxing) {
  447. if (WUXING_RELATION.生[nayin1Wuxing] === nayin2Wuxing || WUXING_RELATION.生[nayin2Wuxing] === nayin1Wuxing) {
  448. score = 80
  449. description = `${nayin1}与${nayin2}纳音相生,天作之合`
  450. } else if (WUXING_RELATION.克[nayin1Wuxing] === nayin2Wuxing || WUXING_RELATION.克[nayin2Wuxing] === nayin1Wuxing) {
  451. score = 45
  452. description = `${nayin1}与${nayin2}纳音相克,需要调和`
  453. } else {
  454. score = 65
  455. description = `${nayin1}与${nayin2}纳音和谐,关系稳定`
  456. }
  457. } else {
  458. description = `${nayin1}与${nayin2},命理互补`
  459. }
  460. }
  461. return {
  462. score: score,
  463. description: description
  464. }
  465. }
  466. /**
  467. * 获取纳音五行
  468. */
  469. function getNayinWuxing(nayin) {
  470. if (nayin.includes('金')) return '金'
  471. if (nayin.includes('木')) return '木'
  472. if (nayin.includes('水')) return '水'
  473. if (nayin.includes('火')) return '火'
  474. if (nayin.includes('土')) return '土'
  475. return null
  476. }
  477. /**
  478. * 分析生肖配对
  479. */
  480. function analyzeShengxiaoMatch(shengxiao1, shengxiao2) {
  481. // 生肖配对表
  482. const shengxiaoCompatibility = {
  483. '鼠': { '龙': 90, '猴': 85, '牛': 80, '马': 30, '羊': 40 },
  484. '牛': { '蛇': 90, '鸡': 85, '鼠': 80, '马': 35, '羊': 30 },
  485. '虎': { '马': 90, '狗': 85, '猪': 80, '猴': 30, '蛇': 35 },
  486. '兔': { '羊': 90, '猪': 85, '狗': 80, '鸡': 30, '龙': 35 },
  487. '龙': { '鼠': 90, '猴': 85, '鸡': 80, '狗': 30, '兔': 35 },
  488. '蛇': { '牛': 90, '鸡': 85, '猴': 80, '猪': 30, '虎': 35 },
  489. '马': { '虎': 90, '狗': 85, '羊': 80, '鼠': 30, '牛': 35 },
  490. '羊': { '兔': 90, '马': 80, '猪': 85, '牛': 30, '鼠': 40 },
  491. '猴': { '鼠': 85, '龙': 85, '蛇': 80, '虎': 30, '猪': 35 },
  492. '鸡': { '牛': 85, '蛇': 85, '龙': 80, '兔': 30, '狗': 35 },
  493. '狗': { '虎': 85, '马': 85, '兔': 80, '龙': 30, '鸡': 35 },
  494. '猪': { '兔': 85, '羊': 85, '虎': 80, '蛇': 30, '猴': 35 }
  495. }
  496. let score = 60
  497. let description = ''
  498. if (shengxiao1 === shengxiao2) {
  499. score = 70
  500. description = `同属${shengxiao1},性格相似,容易产生共鸣`
  501. } else if (shengxiaoCompatibility[shengxiao1] && shengxiaoCompatibility[shengxiao1][shengxiao2]) {
  502. score = shengxiaoCompatibility[shengxiao1][shengxiao2]
  503. if (score >= 85) {
  504. description = `${shengxiao1}与${shengxiao2}是最佳配对,天生一对`
  505. } else if (score >= 70) {
  506. description = `${shengxiao1}与${shengxiao2}配对良好,相处融洽`
  507. } else {
  508. description = `${shengxiao1}与${shengxiao2}需要更多磨合,相互理解`
  509. }
  510. } else {
  511. description = `${shengxiao1}与${shengxiao2}属于中性配对,平稳发展`
  512. }
  513. return {
  514. score: score,
  515. description: description
  516. }
  517. }
  518. /**
  519. * 计算总体配对分数
  520. */
  521. function calculateMatchScore(riganMatch, wuxingMatch, nayinMatch, shengxiaoMatch) {
  522. // 加权计算
  523. const weights = {
  524. rigan: 0.3, // 日干权重30%
  525. wuxing: 0.3, // 五行权重30%
  526. nayin: 0.2, // 纳音权重20%
  527. shengxiao: 0.2 // 生肖权重20%
  528. }
  529. const totalScore =
  530. riganMatch.score * weights.rigan +
  531. wuxingMatch.score * weights.wuxing +
  532. nayinMatch.score * weights.nayin +
  533. shengxiaoMatch.score * weights.shengxiao
  534. return Math.round(totalScore)
  535. }
  536. /**
  537. * 获取配对等级
  538. */
  539. function getMatchLevel(score) {
  540. if (score >= 90) {
  541. return { name: '天作之合', color: 'linear-gradient(135deg, #FF6B9D 0%, #FFA5C6 100%)' }
  542. } else if (score >= 80) {
  543. return { name: '绝配佳偶', color: 'linear-gradient(135deg, #4CAF50 0%, #81C784 100%)' }
  544. } else if (score >= 70) {
  545. return { name: '良缘美眷', color: 'linear-gradient(135deg, #2196F3 0%, #64B5F6 100%)' }
  546. } else if (score >= 60) {
  547. return { name: '尚可之配', color: 'linear-gradient(135deg, #FF9800 0%, #FFB74D 100%)' }
  548. } else if (score >= 50) {
  549. return { name: '需要磨合', color: 'linear-gradient(135deg, #FFC107 0%, #FFD54F 100%)' }
  550. } else {
  551. return { name: '挑战较大', color: 'linear-gradient(135deg, #F44336 0%, #EF5350 100%)' }
  552. }
  553. }
  554. /**
  555. * 生成配对优势
  556. */
  557. function generateMatchAdvantages(bazi1, bazi2) {
  558. const advantages = []
  559. // 分析共同特点
  560. const commonTraits = bazi1.analysis.personality.filter(trait =>
  561. bazi2.analysis.personality.includes(trait)
  562. )
  563. if (commonTraits.length > 0) {
  564. advantages.push(`你们都具有${commonTraits.join('、')}的特质,容易产生共鸣和理解`)
  565. }
  566. // 分析互补特点
  567. if (bazi1.analysis.qiangRuo.type === '身旺' && bazi2.analysis.qiangRuo.type === '身弱') {
  568. advantages.push('一强一弱的组合,能够互相补充,形成良好的平衡')
  569. } else if (bazi1.analysis.qiangRuo.type === '身弱' && bazi2.analysis.qiangRuo.type === '身旺') {
  570. advantages.push('强弱互补的搭配,能够相互扶持,共同成长')
  571. }
  572. // 分析用神关系
  573. if (bazi1.analysis.yongshen.primary === bazi2.riganWuxing) {
  574. advantages.push('对方的日主五行正好是您的用神,能够给您带来很好的帮助')
  575. }
  576. return advantages.length > 0 ? advantages : ['你们的八字搭配有其独特的魅力,能够在相处中发现彼此的闪光点']
  577. }
  578. /**
  579. * 生成配对挑战
  580. */
  581. function generateMatchChallenges(bazi1, bazi2) {
  582. const challenges = []
  583. // 分析冲突
  584. if (bazi1.riganWuxing && bazi2.riganWuxing &&
  585. WUXING_RELATION.克[bazi1.riganWuxing] === bazi2.riganWuxing) {
  586. challenges.push('日主五行相克,在性格和处事方式上可能存在一些分歧')
  587. }
  588. // 分析过旺或过弱
  589. if (bazi1.analysis.qiangRuo.type === '身旺' && bazi2.analysis.qiangRuo.type === '身旺') {
  590. challenges.push('双方都比较强势,在决策时可能需要更多的沟通和协调')
  591. } else if (bazi1.analysis.qiangRuo.type === '身弱' && bazi2.analysis.qiangRuo.type === '身弱') {
  592. challenges.push('双方都比较内敛,需要主动创造更多的交流机会')
  593. }
  594. return challenges.length > 0 ? challenges : ['每一对组合都有需要磨合的地方,这正是相处的乐趣所在']
  595. }
  596. /**
  597. * 生成配对建议
  598. */
  599. function generateMatchSuggestions(bazi1, bazi2, totalScore) {
  600. const suggestions = []
  601. if (totalScore >= 80) {
  602. suggestions.push('你们的八字配对很好,珍惜这份缘分,相互支持共同成长')
  603. } else if (totalScore >= 60) {
  604. suggestions.push('你们的配对有很好的基础,多一些包容和理解,感情会更加稳定')
  605. } else {
  606. suggestions.push('缘分需要用心经营,多沟通多理解,用爱化解所有的不合')
  607. }
  608. // 根据用神给建议
  609. if (bazi1.analysis.yongshen.primary && bazi2.analysis.yongshen.primary) {
  610. const commonYongshen = bazi1.analysis.yongshen.primary === bazi2.analysis.yongshen.primary
  611. if (commonYongshen) {
  612. suggestions.push(`你们的用神都是${bazi1.analysis.yongshen.primary},可以一起从事相关的活动来增进感情`)
  613. }
  614. }
  615. suggestions.push('建议选择有利于双方用神的环境和时间进行重要的决策和沟通')
  616. return suggestions
  617. }
  618. /**
  619. * 生成配对总结
  620. */
  621. function generateMatchSummary(bazi1, bazi2, totalScore) {
  622. const level = getMatchLevel(totalScore)
  623. const summary = `您的八字${bazi1.baziString}与对方的八字${bazi2.baziString},` +
  624. `综合评分${totalScore}分,属于"${level.name}"的配对等级。`
  625. if (totalScore >= 80) {
  626. return summary + '你们的八字搭配很好,是难得的良缘,要好好珍惜。'
  627. } else if (totalScore >= 60) {
  628. return summary + '你们有着不错的缘分基础,通过相互理解和包容,能够建立美好的关系。'
  629. } else {
  630. return summary + '虽然八字配对有些挑战,但真爱能够化解一切,只要相互理解,同样能够获得幸福。'
  631. }
  632. }
  633. /**
  634. * 智能API选择器 - 根据策略选择最佳API
  635. * @returns {Object} 选中的API配置
  636. */
  637. function selectBestAPI() {
  638. const settings = BAZI_API_CONFIG.SETTINGS
  639. const enabledAPIs = settings.ENABLED_APIS
  640. // 按优先级排序可用的API
  641. const sortedAPIs = enabledAPIs
  642. .map(apiName => ({
  643. name: apiName,
  644. config: BAZI_API_CONFIG[apiName],
  645. isConfigured: BAZI_API_CONFIG[apiName].API_KEY &&
  646. !BAZI_API_CONFIG[apiName].API_KEY.startsWith('YOUR_')
  647. }))
  648. .filter(api => api.isConfigured)
  649. .sort((a, b) => a.config.PRIORITY - b.config.PRIORITY)
  650. if (sortedAPIs.length === 0) {
  651. console.log('⚠️ 没有已配置的API,使用本地算法')
  652. return null
  653. }
  654. // 根据策略选择API
  655. switch (settings.STRATEGY) {
  656. case 'cost_first':
  657. return sortedAPIs.sort((a, b) => a.config.COST_PER_CALL - b.config.COST_PER_CALL)[0]
  658. case 'accuracy_first':
  659. return sortedAPIs[0] // 优先级最高的通常最准确
  660. case 'speed_first':
  661. return sortedAPIs.find(api => api.name === 'ALIYUN_API') || sortedAPIs[0]
  662. default:
  663. return sortedAPIs[0]
  664. }
  665. }
  666. /**
  667. * 调用专业八字API获取准确数据(支持多API自动切换)
  668. * @param {Date} birthDate 出生日期
  669. * @param {Number} hour 出生时辰(0-23)
  670. * @returns {Promise<Object>} API返回结果
  671. */
  672. export async function getBaziFromAPI(birthDate, hour) {
  673. const settings = BAZI_API_CONFIG.SETTINGS
  674. const maxRetries = settings.RETRY_COUNT || 2
  675. for (let attempt = 0; attempt < maxRetries + 1; attempt++) {
  676. try {
  677. console.log(`🔍 尝试调用专业八字API (第${attempt + 1}次)`)
  678. console.log('出生时间:', birthDate, '时辰:', hour)
  679. // 智能选择API
  680. const selectedAPI = selectBestAPI()
  681. if (!selectedAPI) {
  682. console.log('⚠️ 没有可用的专业API,使用本地算法')
  683. return null
  684. }
  685. console.log(`📡 选中API: ${selectedAPI.name}`, selectedAPI.config.DESCRIPTION)
  686. const result = await callSpecificAPI(selectedAPI, birthDate, hour)
  687. if (result) {
  688. console.log(`✅ ${selectedAPI.name} 调用成功`)
  689. return {
  690. ...result,
  691. apiProvider: selectedAPI.name,
  692. apiCost: selectedAPI.config.COST_PER_CALL
  693. }
  694. }
  695. // 如果启用自动故障转移,尝试下一个API
  696. if (settings.AUTO_FAILOVER && attempt < maxRetries) {
  697. console.log(`⚠️ ${selectedAPI.name} 调用失败,尝试故障转移`)
  698. // 临时禁用当前API,下次循环会选择下一个
  699. const index = settings.ENABLED_APIS.indexOf(selectedAPI.name)
  700. if (index > -1) {
  701. settings.ENABLED_APIS.splice(index, 1)
  702. }
  703. continue
  704. }
  705. } catch (error) {
  706. console.error(`❌ API调用异常 (尝试${attempt + 1}):`, error)
  707. if (attempt === maxRetries) {
  708. console.log('🔄 所有API均失败,降级到本地算法')
  709. return null
  710. }
  711. }
  712. }
  713. return null
  714. }
  715. /**
  716. * 调用特定的API服务
  717. * @param {Object} selectedAPI 选中的API配置
  718. * @param {Date} birthDate 出生日期
  719. * @param {Number} hour 出生时辰
  720. * @returns {Promise<Object>} API返回结果
  721. */
  722. async function callSpecificAPI(selectedAPI, birthDate, hour) {
  723. const { name, config } = selectedAPI
  724. // 根据不同API构建请求参数
  725. let params, url
  726. switch (name) {
  727. case 'JISU_API':
  728. params = {
  729. appkey: config.API_KEY,
  730. name: '用户', // 姓名参数(必填)
  731. city: '', // 城市参数(必填,可空)
  732. year: birthDate.getFullYear(),
  733. month: birthDate.getMonth() + 1,
  734. day: birthDate.getDate(),
  735. hour: hour,
  736. minute: 0, // 分钟,默认0
  737. sex: 1, // 性别,1男0女,默认男
  738. islunar: 0, // 是否阴历,0阳历1阴历
  739. istaiyang: 0, // 是否太阳时,0不使用1使用
  740. islunarmonth: 2 // 是否闰月,1是2否
  741. }
  742. url = `${config.BASE_URL}${config.ENDPOINTS.bazi}`
  743. break
  744. case 'JUHE_API':
  745. params = {
  746. key: config.API_KEY,
  747. year: birthDate.getFullYear(),
  748. month: birthDate.getMonth() + 1,
  749. day: birthDate.getDate(),
  750. hour: hour
  751. }
  752. url = `${config.BASE_URL}${config.ENDPOINTS.bazi}`
  753. break
  754. case 'ALIYUN_API':
  755. params = {
  756. year: birthDate.getFullYear(),
  757. month: birthDate.getMonth() + 1,
  758. day: birthDate.getDate(),
  759. hour: hour
  760. }
  761. url = `${config.BASE_URL}${config.ENDPOINTS.bazi}`
  762. break
  763. case 'TENCENT_API':
  764. params = {
  765. year: birthDate.getFullYear(),
  766. month: birthDate.getMonth() + 1,
  767. day: birthDate.getDate(),
  768. hour: hour
  769. }
  770. url = `${config.BASE_URL}${config.ENDPOINTS.bazi}`
  771. break
  772. default:
  773. console.error('❌ 不支持的API类型:', name)
  774. return null
  775. }
  776. console.log('📡 API请求地址:', url)
  777. console.log('📡 请求参数:', {
  778. ...params,
  779. [name === 'JISU_API' ? 'appkey' : 'key']: params.appkey || params.key ?
  780. `${(params.appkey || params.key).slice(0, 8)}...` : '未设置'
  781. })
  782. // 发送请求
  783. const response = await new Promise((resolve, reject) => {
  784. const headers = {}
  785. // 阿里云和腾讯云需要特殊的认证头
  786. if (name === 'ALIYUN_API') {
  787. headers['Authorization'] = `APPCODE ${config.API_KEY}`
  788. } else if (name === 'TENCENT_API') {
  789. headers['Authorization'] = config.API_KEY
  790. }
  791. uni.request({
  792. url: url,
  793. data: params,
  794. method: 'GET',
  795. header: headers,
  796. timeout: BAZI_API_CONFIG.SETTINGS.TIMEOUT || 10000,
  797. success: (res) => {
  798. console.log('📡 API响应状态:', res.statusCode)
  799. console.log('📡 API响应数据:', res.data)
  800. resolve(res)
  801. },
  802. fail: (err) => {
  803. console.error('📡 API请求失败:', err)
  804. reject(err)
  805. }
  806. })
  807. })
  808. // 解析不同API的响应格式
  809. return parseAPIResponse(name, response)
  810. }
  811. /**
  812. * 解析不同API的响应格式
  813. * @param {String} apiName API名称
  814. * @param {Object} response 响应对象
  815. * @returns {Object} 标准化的八字数据
  816. */
  817. function parseAPIResponse(apiName, response) {
  818. if (response.statusCode !== 200) {
  819. throw new Error(`HTTP ${response.statusCode}`)
  820. }
  821. const data = response.data
  822. let result = null
  823. switch (apiName) {
  824. case 'JISU_API':
  825. if (data.status === 0 && data.result) {
  826. result = data.result
  827. // 极速数据API返回的是完整的八字排盘数据
  828. console.log('✅ 极速数据返回完整八字排盘信息')
  829. }
  830. break
  831. case 'JUHE_API':
  832. if (data.error_code === 0 && data.result) {
  833. result = data.result
  834. }
  835. break
  836. case 'ALIYUN_API':
  837. case 'TENCENT_API':
  838. if (data.code === 200 && data.data) {
  839. result = data.data
  840. }
  841. break
  842. }
  843. if (!result) {
  844. console.log('⚠️ API返回数据格式异常')
  845. return null
  846. }
  847. // 极速数据API专用解析(更丰富的数据)
  848. if (apiName === 'JISU_API' && result.bazi) {
  849. // 解析八字数组 [年柱, 月柱, 日柱, 时柱]
  850. const baziArray = result.bazi
  851. const nayinArray = result.nayin || []
  852. return {
  853. year: {
  854. gan: baziArray[0]?.charAt(0) || '',
  855. zhi: baziArray[0]?.charAt(1) || '',
  856. ganZhi: baziArray[0] || '',
  857. nayin: nayinArray[0] || ''
  858. },
  859. month: {
  860. gan: baziArray[1]?.charAt(0) || '',
  861. zhi: baziArray[1]?.charAt(1) || '',
  862. ganZhi: baziArray[1] || '',
  863. nayin: nayinArray[1] || ''
  864. },
  865. day: {
  866. gan: baziArray[2]?.charAt(0) || '',
  867. zhi: baziArray[2]?.charAt(1) || '',
  868. ganZhi: baziArray[2] || '',
  869. nayin: nayinArray[2] || ''
  870. },
  871. hour: {
  872. gan: baziArray[3]?.charAt(0) || '',
  873. zhi: baziArray[3]?.charAt(1) || '',
  874. ganZhi: baziArray[3] || '',
  875. nayin: nayinArray[3] || ''
  876. },
  877. baziString: baziArray.join(' '),
  878. // 极速数据提供的专业信息
  879. animal: result.animal, // 生肖
  880. yearganzhi: result.yearganzhi, // 年干支
  881. taiyuan: result.taiyuan, // 胎元
  882. minggong: result.minggong, // 命宫
  883. xunkong: result.xunkong, // 旬空
  884. qiyun: result.qiyun, // 起运时间
  885. jiaoyun: result.jiaoyun, // 交运时间
  886. qiankunzao: result.qiankunzao, // 乾造/坤造
  887. shensha: result.shensha, // 神煞
  888. dayun: result.dayun, // 大运
  889. liunian: result.liunian, // 流年
  890. // 农历信息
  891. lunar: {
  892. year: result.lunaryear,
  893. month: result.lunarmonth,
  894. day: result.lunarday,
  895. hour: result.lunarhour
  896. },
  897. // 节气信息
  898. jieqi: {
  899. prev: result.jieqiprev,
  900. next: result.jieqinext
  901. },
  902. source: 'api',
  903. apiProvider: 'jisuapi',
  904. apiData: result
  905. }
  906. }
  907. // 通用API数据格式(其他API使用)
  908. return {
  909. year: {
  910. gan: result.year_gan || result.yearGan,
  911. zhi: result.year_zhi || result.yearZhi,
  912. ganZhi: (result.year_gan || result.yearGan) + (result.year_zhi || result.yearZhi),
  913. wuxing: result.year_wuxing || result.yearWuxing,
  914. yinyang: result.year_yinyang || result.yearYinyang
  915. },
  916. month: {
  917. gan: result.month_gan || result.monthGan,
  918. zhi: result.month_zhi || result.monthZhi,
  919. ganZhi: (result.month_gan || result.monthGan) + (result.month_zhi || result.monthZhi),
  920. wuxing: result.month_wuxing || result.monthWuxing,
  921. yinyang: result.month_yinyang || result.monthYinyang
  922. },
  923. day: {
  924. gan: result.day_gan || result.dayGan,
  925. zhi: result.day_zhi || result.dayZhi,
  926. ganZhi: (result.day_gan || result.dayGan) + (result.day_zhi || result.dayZhi),
  927. wuxing: result.day_wuxing || result.dayWuxing,
  928. yinyang: result.day_yinyang || result.dayYinyang
  929. },
  930. hour: {
  931. gan: result.hour_gan || result.hourGan,
  932. zhi: result.hour_zhi || result.hourZhi,
  933. ganZhi: (result.hour_gan || result.hourGan) + (result.hour_zhi || result.hourZhi),
  934. wuxing: result.hour_wuxing || result.hourWuxing,
  935. yinyang: result.hour_yinyang || result.hourYinyang
  936. },
  937. baziString: `${result.year_gan || result.yearGan}${result.year_zhi || result.yearZhi} ${result.month_gan || result.monthGan}${result.month_zhi || result.monthZhi} ${result.day_gan || result.dayGan}${result.day_zhi || result.dayZhi} ${result.hour_gan || result.hourGan}${result.hour_zhi || result.hourZhi}`,
  938. nayin: result.nayin || '',
  939. source: 'api',
  940. apiData: result
  941. }
  942. }
  943. /**
  944. * 增强版八字计算(API + 本地算法)
  945. * @param {Date} birthDate 出生日期
  946. * @param {Number} hour 出生时辰(0-23)
  947. * @returns {Object} 八字信息
  948. */
  949. export async function calculateEnhancedBaZi(birthDate, hour) {
  950. let localResult = null
  951. let apiResult = null
  952. try {
  953. // 1. 先计算本地结果
  954. localResult = calculateBaZi(birthDate, hour)
  955. console.log('✅ 本地八字计算完成')
  956. // 2. 尝试获取API结果
  957. if (BAZI_API_CONFIG.SETTINGS.ENABLE_VALIDATION) {
  958. apiResult = await getBaziFromAPI(birthDate, hour)
  959. }
  960. // 3. 结果对比和融合
  961. if (apiResult) {
  962. console.log('🔍 开始对比API与本地算法结果')
  963. const comparison = compareBaziResults(localResult, apiResult)
  964. if (comparison.accuracy >= 0.8) {
  965. console.log('✅ 本地算法准确度高,使用本地结果')
  966. localResult.validation = {
  967. status: 'verified',
  968. accuracy: comparison.accuracy,
  969. apiSource: 'professional',
  970. differences: comparison.differences
  971. }
  972. } else {
  973. console.log('⚠️ 发现差异,使用API结果并标记')
  974. const enhancedResult = mergeApiAndLocalResults(apiResult, localResult)
  975. enhancedResult.validation = {
  976. status: 'api_corrected',
  977. accuracy: comparison.accuracy,
  978. apiSource: 'professional',
  979. differences: comparison.differences
  980. }
  981. return enhancedResult
  982. }
  983. } else {
  984. localResult.validation = {
  985. status: 'local_only',
  986. accuracy: 0.85, // 本地算法估计准确度
  987. apiSource: 'none',
  988. note: '建议配置专业API以提高准确度'
  989. }
  990. }
  991. return localResult
  992. } catch (error) {
  993. console.error('❌ 增强版八字计算异常:', error)
  994. return localResult || calculateBaZi(birthDate, hour)
  995. }
  996. }
  997. /**
  998. * 对比八字计算结果
  999. * @param {Object} localResult 本地算法结果
  1000. * @param {Object} apiResult API算法结果
  1001. * @returns {Object} 对比结果
  1002. */
  1003. function compareBaziResults(localResult, apiResult) {
  1004. const differences = []
  1005. let correctCount = 0
  1006. let totalCount = 8 // 四柱共8个字
  1007. // 对比四柱
  1008. const pillars = ['year', 'month', 'day', 'hour']
  1009. pillars.forEach(pillar => {
  1010. if (localResult[pillar].gan === apiResult[pillar].gan) {
  1011. correctCount++
  1012. } else {
  1013. differences.push(`${pillar}干: 本地${localResult[pillar].gan} vs API${apiResult[pillar].gan}`)
  1014. }
  1015. if (localResult[pillar].zhi === apiResult[pillar].zhi) {
  1016. correctCount++
  1017. } else {
  1018. differences.push(`${pillar}支: 本地${localResult[pillar].zhi} vs API${apiResult[pillar].zhi}`)
  1019. }
  1020. })
  1021. const accuracy = correctCount / totalCount
  1022. return {
  1023. accuracy: accuracy,
  1024. differences: differences,
  1025. recommendation: accuracy >= 0.8 ? 'use_local' : 'use_api'
  1026. }
  1027. }
  1028. /**
  1029. * 融合API和本地结果
  1030. * @param {Object} apiResult API结果
  1031. * @param {Object} localResult 本地结果
  1032. * @returns {Object} 融合后的结果
  1033. */
  1034. function mergeApiAndLocalResults(apiResult, localResult) {
  1035. // 以API结果为准,但保留本地的详细分析
  1036. return {
  1037. ...apiResult,
  1038. // 保留本地的详细分析
  1039. analysis: localResult.analysis,
  1040. birthInfo: {
  1041. ...localResult.birthInfo,
  1042. // 如果API提供了纳音,使用API的
  1043. nayin: apiResult.nayin || localResult.birthInfo.nayin
  1044. },
  1045. // 标记数据来源
  1046. dataSource: 'api_enhanced',
  1047. localCalculation: localResult.baziString,
  1048. apiCalculation: apiResult.baziString
  1049. }
  1050. }
  1051. /**
  1052. * 获取八字建议
  1053. */
  1054. export function getBaziSuggestions(bazi) {
  1055. const suggestions = []
  1056. // 根据验证状态添加可靠性说明
  1057. if (bazi.validation) {
  1058. if (bazi.validation.status === 'verified') {
  1059. suggestions.push(`✅ 您的八字经过专业API验证,准确度${Math.round(bazi.validation.accuracy * 100)}%,结果可信度高`)
  1060. } else if (bazi.validation.status === 'api_corrected') {
  1061. suggestions.push(`🔍 已使用专业算法修正结果,确保测算准确性`)
  1062. } else {
  1063. suggestions.push(`💡 建议配置专业八字API以获得更准确的测算结果`)
  1064. }
  1065. }
  1066. // 根据用神给建议
  1067. const yongshen = bazi.analysis.yongshen.primary
  1068. suggestions.push(`您的用神为${yongshen},建议多接触${yongshen}属性的事物,如颜色、方位、职业等`)
  1069. // 根据强弱给建议
  1070. if (bazi.analysis.qiangRuo.type === '身旺') {
  1071. suggestions.push('您的日主偏强,适合发挥领导才能,但要注意不要过于强势')
  1072. } else if (bazi.analysis.qiangRuo.type === '身弱') {
  1073. suggestions.push('您的日主偏弱,需要多寻求他人的帮助和支持,团队合作会更有利')
  1074. } else {
  1075. suggestions.push('您的日主平衡,适合稳步发展,保持现有的良好状态')
  1076. }
  1077. return suggestions
  1078. }
  1079. // 导出默认对象
  1080. export default {
  1081. calculateBaZi,
  1082. calculateEnhancedBaZi, // 新增:增强版八字计算
  1083. getBaziFromAPI, // 新增:专业API调用
  1084. analyzeBaziMatch,
  1085. getBaziSuggestions,
  1086. TIANGAN,
  1087. DIZHI,
  1088. TIANGAN_WUXING,
  1089. DIZHI_WUXING,
  1090. SHENGXIAO
  1091. }