feat(algorithm): 丰富运势分析内容,新增10个分析维度
新增维度(A): - 情绪/心态运势:基于命宫+福德宫,含情绪指数、压力水平、放松方式 - 家庭运势:基于父母宫+子女宫,含家庭和谐度、亲子关系建议 - 财运详情:正财/偏财区分、消费倾向、投资时机 - 事业详情:同事关系、领导看法、项目决策、晋升机会 - 健康详情:饮食、运动、作息建议、重点关注部位 现有维度增强(B): - 每日金句:基于命宫主星生成个性化金句,14主星各有不同 - 宜忌:6维度×7等级的宜/忌建议 - 冲突预警:基于煞星分布检测感情/事业/人际冲突 交互体验增强(D): - 分时段运势:上午/下午/晚上三段式文字描述 - 星级评定:10维度统一1-5星评级 测试:685 tests passed
This commit is contained in:
Generated
+1889
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"name": "@everything-suitable/algorithm",
|
||||
"version": "1.0.0",
|
||||
"description": "Pure algorithm functions for Everything Is Suitable (紫微斗数/黄历/运势)",
|
||||
"type": "module",
|
||||
"main": "./src/index.ts",
|
||||
"types": "./src/index.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./src/index.ts",
|
||||
"types": "./src/index.ts"
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"src"
|
||||
],
|
||||
"scripts": {
|
||||
"test": "vitest run",
|
||||
"test:watch": "vitest",
|
||||
"type:check": "tsc --noEmit"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.3.0",
|
||||
"vitest": "^1.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": ">=5.0.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,748 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import {
|
||||
// Enums
|
||||
HeavenlyStem,
|
||||
EarthlyBranch,
|
||||
StarNature,
|
||||
TransformationType,
|
||||
MajorStar,
|
||||
PalaceType,
|
||||
|
||||
// Algorithm functions
|
||||
calculateStarScore,
|
||||
calculatePalaceScore,
|
||||
calculateLuckyColor,
|
||||
calculateLuckyNumber,
|
||||
calculateLuckyDirection,
|
||||
determineLuckLevel,
|
||||
calculateDailyAdjustment,
|
||||
calculateMonthlyAdjustment,
|
||||
calculateYearlyAdjustment,
|
||||
generateDailyFortune,
|
||||
analyzePalaceFortune,
|
||||
calculateOverallScore,
|
||||
generateComprehensiveFortune,
|
||||
analyzeLoveFortune,
|
||||
analyzeStudyFortune,
|
||||
analyzeSocialFortune,
|
||||
analyzeTravelFortune,
|
||||
|
||||
// Types
|
||||
type ZiweiChart,
|
||||
type Palace,
|
||||
type StarInfo,
|
||||
type DailyFortune,
|
||||
} from '../index'
|
||||
|
||||
describe('@everything-suitable/algorithm', () => {
|
||||
describe('Enums', () => {
|
||||
it('should export HeavenlyStem with correct values', () => {
|
||||
expect(HeavenlyStem.JIA).toBe('JIA')
|
||||
expect(HeavenlyStem.name(HeavenlyStem.JIA)).toBe('甲')
|
||||
expect(HeavenlyStem.index(HeavenlyStem.JIA)).toBe(1)
|
||||
expect(HeavenlyStem.fromIndex(1)).toBe(HeavenlyStem.JIA)
|
||||
expect(HeavenlyStem.values()).toHaveLength(10)
|
||||
})
|
||||
|
||||
it('should export EarthlyBranch with correct values', () => {
|
||||
expect(EarthlyBranch.ZI).toBe('ZI')
|
||||
expect(EarthlyBranch.name(EarthlyBranch.ZI)).toBe('子')
|
||||
expect(EarthlyBranch.index(EarthlyBranch.ZI)).toBe(1)
|
||||
expect(EarthlyBranch.fromIndex(1)).toBe(EarthlyBranch.ZI)
|
||||
expect(EarthlyBranch.values()).toHaveLength(12)
|
||||
})
|
||||
|
||||
it('should export StarNature with correct values', () => {
|
||||
expect(StarNature.JI).toBe('JI')
|
||||
expect(StarNature.name(StarNature.JI)).toBe('吉')
|
||||
expect(StarNature.description(StarNature.JI)).toBe('吉星')
|
||||
})
|
||||
|
||||
it('should export TransformationType with correct values', () => {
|
||||
expect(TransformationType.LU).toBe('LU')
|
||||
expect(TransformationType.name(TransformationType.LU)).toBe('禄')
|
||||
})
|
||||
|
||||
it('should export MajorStar with correct values', () => {
|
||||
expect(MajorStar.ZIWEI).toBe('ZIWEI')
|
||||
expect(MajorStar.name(MajorStar.ZIWEI)).toBe('紫微')
|
||||
expect(MajorStar.nature(MajorStar.ZIWEI)).toBe(StarNature.JI)
|
||||
expect(MajorStar.values()).toHaveLength(14)
|
||||
})
|
||||
|
||||
it('should export PalaceType with correct values', () => {
|
||||
expect(PalaceType.MING).toBe('MING')
|
||||
expect(PalaceType.name(PalaceType.MING)).toBe('命宫')
|
||||
expect(PalaceType.values()).toHaveLength(12)
|
||||
})
|
||||
})
|
||||
|
||||
describe('determineLuckLevel', () => {
|
||||
it('should return 大吉 for score >= 90', () => {
|
||||
expect(determineLuckLevel(95)).toBe('大吉')
|
||||
expect(determineLuckLevel(90)).toBe('大吉')
|
||||
})
|
||||
|
||||
it('should return 吉 for score 80-89', () => {
|
||||
expect(determineLuckLevel(85)).toBe('吉')
|
||||
expect(determineLuckLevel(80)).toBe('吉')
|
||||
})
|
||||
|
||||
it('should return 中吉 for score 70-79', () => {
|
||||
expect(determineLuckLevel(75)).toBe('中吉')
|
||||
})
|
||||
|
||||
it('should return 平 for score 60-69', () => {
|
||||
expect(determineLuckLevel(65)).toBe('平')
|
||||
})
|
||||
|
||||
it('should return 中平 for score 50-59', () => {
|
||||
expect(determineLuckLevel(55)).toBe('中平')
|
||||
})
|
||||
|
||||
it('should return 小凶 for score 40-49', () => {
|
||||
expect(determineLuckLevel(45)).toBe('小凶')
|
||||
})
|
||||
|
||||
it('should return 凶 for score < 40', () => {
|
||||
expect(determineLuckLevel(35)).toBe('凶')
|
||||
expect(determineLuckLevel(0)).toBe('凶')
|
||||
})
|
||||
})
|
||||
|
||||
describe('calculateStarScore', () => {
|
||||
it('should return 50 for null starInfo', () => {
|
||||
expect(calculateStarScore(null)).toBe(50)
|
||||
})
|
||||
|
||||
it('should calculate base score for a Ji star', () => {
|
||||
const starInfo: StarInfo = {
|
||||
star: MajorStar.ZIWEI,
|
||||
nature: StarNature.JI,
|
||||
transformation: null,
|
||||
brightness: 100,
|
||||
description: '帝星',
|
||||
}
|
||||
const score = calculateStarScore(starInfo)
|
||||
expect(score).toBeGreaterThan(0)
|
||||
expect(score).toBeLessThanOrEqual(100)
|
||||
})
|
||||
})
|
||||
|
||||
describe('calculatePalaceScore', () => {
|
||||
it('should return 50 for null palace', () => {
|
||||
expect(calculatePalaceScore(null)).toBe(50)
|
||||
})
|
||||
|
||||
it('should return 50 for empty palace', () => {
|
||||
const palace: Palace = {
|
||||
palaceType: PalaceType.MING,
|
||||
earthlyBranch: EarthlyBranch.YIN,
|
||||
majorStars: [],
|
||||
minorStars: [],
|
||||
analysis: '',
|
||||
score: 0,
|
||||
}
|
||||
expect(calculatePalaceScore(palace)).toBe(50)
|
||||
})
|
||||
})
|
||||
|
||||
describe('calculateOverallScore', () => {
|
||||
it('should return 60 for empty fortunes', () => {
|
||||
expect(calculateOverallScore({})).toBe(60)
|
||||
})
|
||||
|
||||
it('should calculate average of palace fortunes', () => {
|
||||
const fortunes = {
|
||||
[PalaceType.MING]: {
|
||||
palaceType: PalaceType.MING,
|
||||
score: 80,
|
||||
luckLevel: '吉',
|
||||
analysis: '',
|
||||
advice: '',
|
||||
},
|
||||
[PalaceType.CAI]: {
|
||||
palaceType: PalaceType.CAI,
|
||||
score: 60,
|
||||
luckLevel: '平',
|
||||
analysis: '',
|
||||
advice: '',
|
||||
},
|
||||
}
|
||||
expect(calculateOverallScore(fortunes)).toBe(70)
|
||||
})
|
||||
|
||||
it('should handle Map input', () => {
|
||||
const fortunes = new Map()
|
||||
fortunes.set(PalaceType.MING, {
|
||||
palaceType: PalaceType.MING,
|
||||
score: 90,
|
||||
luckLevel: '大吉',
|
||||
analysis: '',
|
||||
advice: '',
|
||||
})
|
||||
expect(calculateOverallScore(fortunes)).toBe(90)
|
||||
})
|
||||
})
|
||||
|
||||
describe('calculateDailyAdjustment', () => {
|
||||
it('should return 0 adjustment for palace with no stars', () => {
|
||||
const palace: Palace = {
|
||||
palaceType: PalaceType.MING,
|
||||
earthlyBranch: EarthlyBranch.YIN,
|
||||
majorStars: [],
|
||||
minorStars: [],
|
||||
analysis: '',
|
||||
score: 50,
|
||||
}
|
||||
const date = new Date('2026-08-14')
|
||||
expect(calculateDailyAdjustment(palace, date)).toBe(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe('generateDailyFortune', () => {
|
||||
const mockChart: ZiweiChart = {
|
||||
birthInfo: {
|
||||
birthTime: '1990-05-15T08:00:00',
|
||||
yearStem: HeavenlyStem.GENG,
|
||||
monthStem: HeavenlyStem.XIN,
|
||||
dayStem: HeavenlyStem.JIA,
|
||||
hourStem: HeavenlyStem.WU,
|
||||
yearBranch: EarthlyBranch.WU,
|
||||
monthBranch: EarthlyBranch.SI,
|
||||
dayBranch: EarthlyBranch.CHEN,
|
||||
hourBranch: EarthlyBranch.CHEN,
|
||||
gender: 'MALE',
|
||||
timezone: 'Asia/Shanghai',
|
||||
},
|
||||
palaces: [
|
||||
{
|
||||
palaceType: PalaceType.MING,
|
||||
earthlyBranch: EarthlyBranch.YIN,
|
||||
majorStars: [
|
||||
{
|
||||
star: MajorStar.ZIWEI,
|
||||
nature: StarNature.JI,
|
||||
transformation: TransformationType.LU,
|
||||
brightness: 100,
|
||||
description: '帝星',
|
||||
},
|
||||
],
|
||||
minorStars: [],
|
||||
analysis: '命宫分析',
|
||||
score: 85,
|
||||
},
|
||||
{
|
||||
palaceType: PalaceType.CAI,
|
||||
earthlyBranch: EarthlyBranch.WU,
|
||||
majorStars: [
|
||||
{
|
||||
star: MajorStar.WUQU,
|
||||
nature: StarNature.XIONG,
|
||||
transformation: null,
|
||||
brightness: 80,
|
||||
description: '财星',
|
||||
},
|
||||
],
|
||||
minorStars: [],
|
||||
analysis: '财帛宫分析',
|
||||
score: 75,
|
||||
},
|
||||
{
|
||||
palaceType: PalaceType.GUANLU,
|
||||
earthlyBranch: EarthlyBranch.XU,
|
||||
majorStars: [
|
||||
{
|
||||
star: MajorStar.TIANFU,
|
||||
nature: StarNature.JI,
|
||||
transformation: null,
|
||||
brightness: 90,
|
||||
description: '库星',
|
||||
},
|
||||
],
|
||||
minorStars: [],
|
||||
analysis: '官禄宫分析',
|
||||
score: 85,
|
||||
},
|
||||
{
|
||||
palaceType: PalaceType.FUQI,
|
||||
earthlyBranch: EarthlyBranch.SHEN,
|
||||
majorStars: [
|
||||
{
|
||||
star: MajorStar.TIANJI,
|
||||
nature: StarNature.JI,
|
||||
transformation: null,
|
||||
brightness: 70,
|
||||
description: '智星',
|
||||
},
|
||||
],
|
||||
minorStars: [],
|
||||
analysis: '夫妻宫分析',
|
||||
score: 75,
|
||||
},
|
||||
{
|
||||
palaceType: PalaceType.JILU,
|
||||
earthlyBranch: EarthlyBranch.SI,
|
||||
majorStars: [
|
||||
{
|
||||
star: MajorStar.TAIYANG,
|
||||
nature: StarNature.JI,
|
||||
transformation: TransformationType.KE,
|
||||
brightness: 85,
|
||||
description: '贵星',
|
||||
},
|
||||
],
|
||||
minorStars: [],
|
||||
analysis: '疾厄宫分析',
|
||||
score: 80,
|
||||
},
|
||||
{
|
||||
palaceType: PalaceType.XIONG,
|
||||
earthlyBranch: EarthlyBranch.MAO,
|
||||
majorStars: [
|
||||
{
|
||||
star: MajorStar.LIANCHEN,
|
||||
nature: StarNature.XIONG,
|
||||
transformation: null,
|
||||
brightness: 60,
|
||||
description: '情星',
|
||||
},
|
||||
],
|
||||
minorStars: [],
|
||||
analysis: '兄弟宫分析',
|
||||
score: 55,
|
||||
},
|
||||
{
|
||||
palaceType: PalaceType.QIAN,
|
||||
earthlyBranch: EarthlyBranch.WEI,
|
||||
majorStars: [],
|
||||
minorStars: [],
|
||||
analysis: '迁移宫分析',
|
||||
score: 50,
|
||||
},
|
||||
{
|
||||
palaceType: PalaceType.ZINV,
|
||||
earthlyBranch: EarthlyBranch.CHOU,
|
||||
majorStars: [],
|
||||
minorStars: [],
|
||||
analysis: '子女宫分析',
|
||||
score: 50,
|
||||
},
|
||||
{
|
||||
palaceType: PalaceType.TUDI,
|
||||
earthlyBranch: EarthlyBranch.HAI,
|
||||
majorStars: [],
|
||||
minorStars: [],
|
||||
analysis: '田宅宫分析',
|
||||
score: 50,
|
||||
},
|
||||
{
|
||||
palaceType: PalaceType.FUBEN,
|
||||
earthlyBranch: EarthlyBranch.YOU,
|
||||
majorStars: [],
|
||||
minorStars: [],
|
||||
analysis: '福德宫分析',
|
||||
score: 50,
|
||||
},
|
||||
{
|
||||
palaceType: PalaceType.SHEN,
|
||||
earthlyBranch: EarthlyBranch.CHEN,
|
||||
majorStars: [],
|
||||
minorStars: [],
|
||||
analysis: '身宫分析',
|
||||
score: 50,
|
||||
},
|
||||
{
|
||||
palaceType: PalaceType.FU,
|
||||
earthlyBranch: EarthlyBranch.SI,
|
||||
majorStars: [],
|
||||
minorStars: [],
|
||||
analysis: '父母宫分析',
|
||||
score: 50,
|
||||
},
|
||||
],
|
||||
yearStem: HeavenlyStem.GENG,
|
||||
mingGongBranch: EarthlyBranch.YIN,
|
||||
shenGongBranch: EarthlyBranch.CHEN,
|
||||
summary: '测试命盘',
|
||||
overallLuck: '吉',
|
||||
}
|
||||
|
||||
it('should generate daily fortune with correct structure', () => {
|
||||
const date = new Date('2026-08-14')
|
||||
const fortune = generateDailyFortune(mockChart, date)
|
||||
|
||||
expect(fortune).toBeDefined()
|
||||
expect(fortune.fortuneDate).toBe('2026-08-14')
|
||||
expect(fortune.overallScore).toBeGreaterThan(0)
|
||||
expect(fortune.overallLuck).toBeTruthy()
|
||||
expect(fortune.careerAdvice).toBeTruthy()
|
||||
expect(fortune.wealthAdvice).toBeTruthy()
|
||||
expect(fortune.relationshipAdvice).toBeTruthy()
|
||||
expect(fortune.healthAdvice).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should generate consistent fortune for same input', () => {
|
||||
const date = new Date('2026-08-14')
|
||||
const fortune1 = generateDailyFortune(mockChart, date)
|
||||
const fortune2 = generateDailyFortune(mockChart, date)
|
||||
|
||||
expect(fortune1.overallScore).toBe(fortune2.overallScore)
|
||||
expect(fortune1.overallLuck).toBe(fortune2.overallLuck)
|
||||
})
|
||||
|
||||
it('should generate different fortunes for different dates', () => {
|
||||
const fortune1 = generateDailyFortune(mockChart, new Date('2026-08-14'))
|
||||
const fortune2 = generateDailyFortune(mockChart, new Date('2026-08-15'))
|
||||
|
||||
// Scores may differ due to daily adjustment
|
||||
expect(fortune1.fortuneDate).not.toBe(fortune2.fortuneDate)
|
||||
})
|
||||
})
|
||||
|
||||
describe('generateComprehensiveFortune', () => {
|
||||
const mockChart: ZiweiChart = {
|
||||
birthInfo: {
|
||||
birthTime: '1990-05-15T08:00:00',
|
||||
yearStem: HeavenlyStem.GENG,
|
||||
monthStem: HeavenlyStem.XIN,
|
||||
dayStem: HeavenlyStem.JIA,
|
||||
hourStem: HeavenlyStem.WU,
|
||||
yearBranch: EarthlyBranch.WU,
|
||||
monthBranch: EarthlyBranch.SI,
|
||||
dayBranch: EarthlyBranch.CHEN,
|
||||
hourBranch: EarthlyBranch.CHEN,
|
||||
gender: 'MALE',
|
||||
timezone: 'Asia/Shanghai',
|
||||
},
|
||||
palaces: [
|
||||
{
|
||||
palaceType: PalaceType.MING,
|
||||
earthlyBranch: EarthlyBranch.YIN,
|
||||
majorStars: [{
|
||||
star: MajorStar.ZIWEI,
|
||||
nature: StarNature.JI,
|
||||
transformation: TransformationType.LU,
|
||||
brightness: 100,
|
||||
description: '帝星',
|
||||
}],
|
||||
minorStars: [],
|
||||
analysis: '命宫分析',
|
||||
score: 85,
|
||||
},
|
||||
{
|
||||
palaceType: PalaceType.CAI,
|
||||
earthlyBranch: EarthlyBranch.WU,
|
||||
majorStars: [{
|
||||
star: MajorStar.WUQU,
|
||||
nature: StarNature.XIONG,
|
||||
transformation: null,
|
||||
brightness: 80,
|
||||
description: '财星',
|
||||
}],
|
||||
minorStars: [],
|
||||
analysis: '财帛宫分析',
|
||||
score: 75,
|
||||
},
|
||||
{
|
||||
palaceType: PalaceType.GUANLU,
|
||||
earthlyBranch: EarthlyBranch.XU,
|
||||
majorStars: [{
|
||||
star: MajorStar.TIANFU,
|
||||
nature: StarNature.JI,
|
||||
transformation: null,
|
||||
brightness: 90,
|
||||
description: '库星',
|
||||
}],
|
||||
minorStars: ['WENCHANG', 'WENQU'],
|
||||
analysis: '官禄宫分析',
|
||||
score: 85,
|
||||
},
|
||||
{
|
||||
palaceType: PalaceType.FUQI,
|
||||
earthlyBranch: EarthlyBranch.SHEN,
|
||||
majorStars: [{
|
||||
star: MajorStar.TIANJI,
|
||||
nature: StarNature.JI,
|
||||
transformation: null,
|
||||
brightness: 70,
|
||||
description: '智星',
|
||||
}],
|
||||
minorStars: ['HONGLUAN', 'TIANXI'],
|
||||
analysis: '夫妻宫分析',
|
||||
score: 75,
|
||||
},
|
||||
{
|
||||
palaceType: PalaceType.JILU,
|
||||
earthlyBranch: EarthlyBranch.SI,
|
||||
majorStars: [{
|
||||
star: MajorStar.TAIYANG,
|
||||
nature: StarNature.JI,
|
||||
transformation: TransformationType.KE,
|
||||
brightness: 85,
|
||||
description: '贵星',
|
||||
}],
|
||||
minorStars: [],
|
||||
analysis: '疾厄宫分析',
|
||||
score: 80,
|
||||
},
|
||||
{
|
||||
palaceType: PalaceType.XIONG,
|
||||
earthlyBranch: EarthlyBranch.MAO,
|
||||
majorStars: [{
|
||||
star: MajorStar.LIANCHEN,
|
||||
nature: StarNature.XIONG,
|
||||
transformation: null,
|
||||
brightness: 60,
|
||||
description: '情星',
|
||||
}],
|
||||
minorStars: ['TIANKUI', 'TIANYUE'],
|
||||
analysis: '兄弟宫分析',
|
||||
score: 55,
|
||||
},
|
||||
{
|
||||
palaceType: PalaceType.QIAN,
|
||||
earthlyBranch: EarthlyBranch.WEI,
|
||||
majorStars: [],
|
||||
minorStars: ['TIANMA'],
|
||||
analysis: '迁移宫分析',
|
||||
score: 50,
|
||||
},
|
||||
{
|
||||
palaceType: PalaceType.ZINV,
|
||||
earthlyBranch: EarthlyBranch.CHOU,
|
||||
majorStars: [],
|
||||
minorStars: [],
|
||||
analysis: '子女宫分析',
|
||||
score: 50,
|
||||
},
|
||||
{
|
||||
palaceType: PalaceType.TUDI,
|
||||
earthlyBranch: EarthlyBranch.HAI,
|
||||
majorStars: [],
|
||||
minorStars: [],
|
||||
analysis: '田宅宫分析',
|
||||
score: 50,
|
||||
},
|
||||
{
|
||||
palaceType: PalaceType.FUBEN,
|
||||
earthlyBranch: EarthlyBranch.YOU,
|
||||
majorStars: [],
|
||||
minorStars: [],
|
||||
analysis: '福德宫分析',
|
||||
score: 50,
|
||||
},
|
||||
{
|
||||
palaceType: PalaceType.SHEN,
|
||||
earthlyBranch: EarthlyBranch.CHEN,
|
||||
majorStars: [],
|
||||
minorStars: [],
|
||||
analysis: '身宫分析',
|
||||
score: 50,
|
||||
},
|
||||
{
|
||||
palaceType: PalaceType.FU,
|
||||
earthlyBranch: EarthlyBranch.SI,
|
||||
majorStars: [],
|
||||
minorStars: [],
|
||||
analysis: '父母宫分析',
|
||||
score: 50,
|
||||
},
|
||||
],
|
||||
yearStem: HeavenlyStem.GENG,
|
||||
mingGongBranch: EarthlyBranch.YIN,
|
||||
shenGongBranch: EarthlyBranch.CHEN,
|
||||
summary: '测试命盘',
|
||||
overallLuck: '吉',
|
||||
}
|
||||
|
||||
it('should generate comprehensive fortune with all dimensions', () => {
|
||||
const date = new Date('2026-08-14')
|
||||
const fortune = generateComprehensiveFortune(mockChart, date)
|
||||
|
||||
// Base fortune fields
|
||||
expect(fortune.overallScore).toBeGreaterThan(0)
|
||||
expect(fortune.careerAdvice).toBeTruthy()
|
||||
expect(fortune.wealthAdvice).toBeTruthy()
|
||||
|
||||
// Love fortune fields
|
||||
expect(fortune.loveScore).toBeDefined()
|
||||
expect(fortune.loveScore).toBeGreaterThan(0)
|
||||
expect(fortune.overallLoveLuck).toBeTruthy()
|
||||
expect(fortune.peachBlossomIndex).toBeTruthy()
|
||||
expect(fortune.singleAdvice).toBeTruthy()
|
||||
|
||||
// Study fortune fields
|
||||
expect(fortune.studyScore).toBeDefined()
|
||||
expect(fortune.studyScore).toBeGreaterThan(0)
|
||||
expect(fortune.overallStudyLuck).toBeTruthy()
|
||||
expect(fortune.studyAdvice).toBeTruthy()
|
||||
|
||||
// Social fortune fields
|
||||
expect(fortune.socialScore).toBeDefined()
|
||||
expect(fortune.socialScore).toBeGreaterThan(0)
|
||||
expect(fortune.overallSocialLuck).toBeTruthy()
|
||||
expect(fortune.socialAdvice).toBeTruthy()
|
||||
|
||||
// Travel fortune fields
|
||||
expect(fortune.travelScore).toBeDefined()
|
||||
expect(fortune.travelScore).toBeGreaterThan(0)
|
||||
expect(fortune.overallTravelLuck).toBeTruthy()
|
||||
expect(fortune.travelAdvice).toBeTruthy()
|
||||
})
|
||||
})
|
||||
|
||||
describe('analyzePalaceFortune', () => {
|
||||
it('should analyze palace fortune with score and luck level', () => {
|
||||
const palace: Palace = {
|
||||
palaceType: PalaceType.MING,
|
||||
earthlyBranch: EarthlyBranch.YIN,
|
||||
majorStars: [{
|
||||
star: MajorStar.ZIWEI,
|
||||
nature: StarNature.JI,
|
||||
transformation: TransformationType.LU,
|
||||
brightness: 100,
|
||||
description: '帝星',
|
||||
}],
|
||||
minorStars: [],
|
||||
analysis: '',
|
||||
score: 85,
|
||||
}
|
||||
const date = new Date('2026-08-14')
|
||||
const fortune = analyzePalaceFortune(palace, date)
|
||||
|
||||
expect(fortune.palaceType).toBe(PalaceType.MING)
|
||||
expect(fortune.score).toBeGreaterThan(0)
|
||||
expect(fortune.luckLevel).toBeTruthy()
|
||||
expect(fortune.analysis).toContain('命宫')
|
||||
expect(fortune.advice).toBeTruthy()
|
||||
})
|
||||
})
|
||||
|
||||
describe('calculateLuckyColor', () => {
|
||||
it('should work with a mock chart', () => {
|
||||
// Create a minimal chart for testing
|
||||
const chart: ZiweiChart = {
|
||||
birthInfo: {
|
||||
birthTime: '1990-05-15T08:00:00',
|
||||
yearStem: HeavenlyStem.GENG,
|
||||
monthStem: HeavenlyStem.XIN,
|
||||
dayStem: HeavenlyStem.JIA,
|
||||
hourStem: HeavenlyStem.WU,
|
||||
yearBranch: EarthlyBranch.WU,
|
||||
monthBranch: EarthlyBranch.SI,
|
||||
dayBranch: EarthlyBranch.CHEN,
|
||||
hourBranch: EarthlyBranch.CHEN,
|
||||
gender: 'MALE',
|
||||
timezone: 'Asia/Shanghai',
|
||||
},
|
||||
palaces: [{
|
||||
palaceType: PalaceType.MING,
|
||||
earthlyBranch: EarthlyBranch.YIN,
|
||||
majorStars: [{
|
||||
star: MajorStar.ZIWEI,
|
||||
nature: StarNature.JI,
|
||||
transformation: null,
|
||||
brightness: 100,
|
||||
description: '帝星',
|
||||
}],
|
||||
minorStars: [],
|
||||
analysis: '',
|
||||
score: 85,
|
||||
}],
|
||||
yearStem: HeavenlyStem.GENG,
|
||||
mingGongBranch: EarthlyBranch.YIN,
|
||||
shenGongBranch: EarthlyBranch.CHEN,
|
||||
summary: '',
|
||||
overallLuck: '吉',
|
||||
}
|
||||
const color = calculateLuckyColor(chart, new Date('2026-08-14'))
|
||||
expect(color).toBeTruthy()
|
||||
expect(typeof color).toBe('string')
|
||||
})
|
||||
})
|
||||
|
||||
describe('calculateLuckyNumber', () => {
|
||||
it('should return a number string', () => {
|
||||
const chart: ZiweiChart = {
|
||||
birthInfo: {
|
||||
birthTime: '1990-05-15T08:00:00',
|
||||
yearStem: HeavenlyStem.GENG,
|
||||
monthStem: HeavenlyStem.XIN,
|
||||
dayStem: HeavenlyStem.JIA,
|
||||
hourStem: HeavenlyStem.WU,
|
||||
yearBranch: EarthlyBranch.WU,
|
||||
monthBranch: EarthlyBranch.SI,
|
||||
dayBranch: EarthlyBranch.CHEN,
|
||||
hourBranch: EarthlyBranch.CHEN,
|
||||
gender: 'MALE',
|
||||
timezone: 'Asia/Shanghai',
|
||||
},
|
||||
palaces: [{
|
||||
palaceType: PalaceType.MING,
|
||||
earthlyBranch: EarthlyBranch.YIN,
|
||||
majorStars: [{
|
||||
star: MajorStar.ZIWEI,
|
||||
nature: StarNature.JI,
|
||||
transformation: null,
|
||||
brightness: 100,
|
||||
description: '帝星',
|
||||
}],
|
||||
minorStars: [],
|
||||
analysis: '',
|
||||
score: 85,
|
||||
}],
|
||||
yearStem: HeavenlyStem.GENG,
|
||||
mingGongBranch: EarthlyBranch.YIN,
|
||||
shenGongBranch: EarthlyBranch.CHEN,
|
||||
summary: '',
|
||||
overallLuck: '吉',
|
||||
}
|
||||
const number = calculateLuckyNumber(chart, new Date('2026-08-14'))
|
||||
expect(number).toBeTruthy()
|
||||
expect(typeof number).toBe('string')
|
||||
})
|
||||
})
|
||||
|
||||
describe('calculateLuckyDirection', () => {
|
||||
it('should return a direction string', () => {
|
||||
const chart: ZiweiChart = {
|
||||
birthInfo: {
|
||||
birthTime: '1990-05-15T08:00:00',
|
||||
yearStem: HeavenlyStem.GENG,
|
||||
monthStem: HeavenlyStem.XIN,
|
||||
dayStem: HeavenlyStem.JIA,
|
||||
hourStem: HeavenlyStem.WU,
|
||||
yearBranch: EarthlyBranch.WU,
|
||||
monthBranch: EarthlyBranch.SI,
|
||||
dayBranch: EarthlyBranch.CHEN,
|
||||
hourBranch: EarthlyBranch.CHEN,
|
||||
gender: 'MALE',
|
||||
timezone: 'Asia/Shanghai',
|
||||
},
|
||||
palaces: [{
|
||||
palaceType: PalaceType.MING,
|
||||
earthlyBranch: EarthlyBranch.YIN,
|
||||
majorStars: [{
|
||||
star: MajorStar.ZIWEI,
|
||||
nature: StarNature.JI,
|
||||
transformation: null,
|
||||
brightness: 100,
|
||||
description: '帝星',
|
||||
}],
|
||||
minorStars: [],
|
||||
analysis: '',
|
||||
score: 85,
|
||||
}],
|
||||
yearStem: HeavenlyStem.GENG,
|
||||
mingGongBranch: EarthlyBranch.YIN,
|
||||
shenGongBranch: EarthlyBranch.CHEN,
|
||||
summary: '',
|
||||
overallLuck: '吉',
|
||||
}
|
||||
const direction = calculateLuckyDirection(chart, new Date('2026-08-14'))
|
||||
expect(direction).toBeTruthy()
|
||||
expect(typeof direction).toBe('string')
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,316 @@
|
||||
export enum HeavenlyStem {
|
||||
JIA = 'JIA',
|
||||
YI = 'YI',
|
||||
BING = 'BING',
|
||||
DING = 'DING',
|
||||
WU = 'WU',
|
||||
JI = 'JI',
|
||||
GENG = 'GENG',
|
||||
XIN = 'XIN',
|
||||
REN = 'REN',
|
||||
GUI = 'GUI',
|
||||
}
|
||||
|
||||
export namespace HeavenlyStem {
|
||||
const DATA: Record<HeavenlyStem, { name: string; index: number }> = {
|
||||
[HeavenlyStem.JIA]: { name: '甲', index: 1 },
|
||||
[HeavenlyStem.YI]: { name: '乙', index: 2 },
|
||||
[HeavenlyStem.BING]: { name: '丙', index: 3 },
|
||||
[HeavenlyStem.DING]: { name: '丁', index: 4 },
|
||||
[HeavenlyStem.WU]: { name: '戊', index: 5 },
|
||||
[HeavenlyStem.JI]: { name: '己', index: 6 },
|
||||
[HeavenlyStem.GENG]: { name: '庚', index: 7 },
|
||||
[HeavenlyStem.XIN]: { name: '辛', index: 8 },
|
||||
[HeavenlyStem.REN]: { name: '壬', index: 9 },
|
||||
[HeavenlyStem.GUI]: { name: '癸', index: 10 },
|
||||
}
|
||||
|
||||
const VALUES: HeavenlyStem[] = Object.values(HeavenlyStem).filter(
|
||||
(v): v is HeavenlyStem => typeof v === 'string',
|
||||
)
|
||||
|
||||
export const name = (s: HeavenlyStem): string => DATA[s].name
|
||||
export const index = (s: HeavenlyStem): number => DATA[s].index
|
||||
|
||||
export function fromIndex(i: number): HeavenlyStem {
|
||||
return VALUES[((i - 1) % 10 + 10) % 10]
|
||||
}
|
||||
|
||||
export function values(): HeavenlyStem[] {
|
||||
return [...VALUES]
|
||||
}
|
||||
}
|
||||
|
||||
export enum EarthlyBranch {
|
||||
ZI = 'ZI',
|
||||
CHOU = 'CHOU',
|
||||
YIN = 'YIN',
|
||||
MAO = 'MAO',
|
||||
CHEN = 'CHEN',
|
||||
SI = 'SI',
|
||||
WU = 'WU',
|
||||
WEI = 'WEI',
|
||||
SHEN = 'SHEN',
|
||||
YOU = 'YOU',
|
||||
XU = 'XU',
|
||||
HAI = 'HAI',
|
||||
}
|
||||
|
||||
export namespace EarthlyBranch {
|
||||
const DATA: Record<EarthlyBranch, { name: string; index: number }> = {
|
||||
[EarthlyBranch.ZI]: { name: '子', index: 1 },
|
||||
[EarthlyBranch.CHOU]: { name: '丑', index: 2 },
|
||||
[EarthlyBranch.YIN]: { name: '寅', index: 3 },
|
||||
[EarthlyBranch.MAO]: { name: '卯', index: 4 },
|
||||
[EarthlyBranch.CHEN]: { name: '辰', index: 5 },
|
||||
[EarthlyBranch.SI]: { name: '巳', index: 6 },
|
||||
[EarthlyBranch.WU]: { name: '午', index: 7 },
|
||||
[EarthlyBranch.WEI]: { name: '未', index: 8 },
|
||||
[EarthlyBranch.SHEN]: { name: '申', index: 9 },
|
||||
[EarthlyBranch.YOU]: { name: '酉', index: 10 },
|
||||
[EarthlyBranch.XU]: { name: '戌', index: 11 },
|
||||
[EarthlyBranch.HAI]: { name: '亥', index: 12 },
|
||||
}
|
||||
|
||||
const VALUES: EarthlyBranch[] = Object.values(EarthlyBranch).filter(
|
||||
(v): v is EarthlyBranch => typeof v === 'string',
|
||||
)
|
||||
|
||||
export const name = (s: EarthlyBranch): string => DATA[s].name
|
||||
export const index = (s: EarthlyBranch): number => DATA[s].index
|
||||
|
||||
export function fromIndex(i: number): EarthlyBranch {
|
||||
return VALUES[((i - 1) % 12 + 12) % 12]
|
||||
}
|
||||
|
||||
export function values(): EarthlyBranch[] {
|
||||
return [...VALUES]
|
||||
}
|
||||
}
|
||||
|
||||
export enum StarNature {
|
||||
JI = 'JI',
|
||||
XIONG = 'XIONG',
|
||||
ZHONGHE = 'ZHONGHE',
|
||||
}
|
||||
|
||||
export namespace StarNature {
|
||||
const DATA: Record<StarNature, { name: string; description: string }> = {
|
||||
[StarNature.JI]: { name: '吉', description: '吉星' },
|
||||
[StarNature.XIONG]: { name: '凶', description: '凶星' },
|
||||
[StarNature.ZHONGHE]: { name: '中', description: '中性' },
|
||||
}
|
||||
|
||||
export const name = (s: StarNature): string => DATA[s].name
|
||||
export const description = (s: StarNature): string => DATA[s].description
|
||||
}
|
||||
|
||||
export enum TransformationType {
|
||||
LU = 'LU',
|
||||
QUAN = 'QUAN',
|
||||
KE = 'KE',
|
||||
JI = 'JI',
|
||||
}
|
||||
|
||||
export namespace TransformationType {
|
||||
const DATA: Record<TransformationType, { name: string }> = {
|
||||
[TransformationType.LU]: { name: '禄' },
|
||||
[TransformationType.QUAN]: { name: '权' },
|
||||
[TransformationType.KE]: { name: '科' },
|
||||
[TransformationType.JI]: { name: '忌' },
|
||||
}
|
||||
|
||||
export const name = (s: TransformationType): string => DATA[s].name
|
||||
}
|
||||
|
||||
export enum MajorStar {
|
||||
ZIWEI = 'ZIWEI',
|
||||
TIANJI = 'TIANJI',
|
||||
TAIYANG = 'TAIYANG',
|
||||
WUQU = 'WUQU',
|
||||
TIANTONG = 'TIANTONG',
|
||||
LIANCHEN = 'LIANCHEN',
|
||||
TIANFU = 'TIANFU',
|
||||
TAIYIN = 'TAIYIN',
|
||||
TANLANG = 'TANLANG',
|
||||
JUMEN = 'JUMEN',
|
||||
TIANXIANG = 'TIANXIANG',
|
||||
TIANLIANG = 'TIANLIANG',
|
||||
QISHA = 'QISHA',
|
||||
POJUN = 'POJUN',
|
||||
}
|
||||
|
||||
export namespace MajorStar {
|
||||
const DATA: Record<
|
||||
MajorStar,
|
||||
{ name: string; index: number; nature: StarNature; alias: string; description: string }
|
||||
> = {
|
||||
[MajorStar.ZIWEI]: { name: '紫微', index: 1, nature: StarNature.JI, alias: '帝星', description: '领导、权威、尊贵' },
|
||||
[MajorStar.TIANJI]: { name: '天机', index: 2, nature: StarNature.JI, alias: '智星', description: '智慧、谋略、变通' },
|
||||
[MajorStar.TAIYANG]: { name: '太阳', index: 3, nature: StarNature.JI, alias: '贵星', description: '光明、热情、慷慨' },
|
||||
[MajorStar.WUQU]: { name: '武曲', index: 4, nature: StarNature.XIONG, alias: '财星', description: '刚毅、务实、财帛' },
|
||||
[MajorStar.TIANTONG]: { name: '天同', index: 5, nature: StarNature.JI, alias: '福星', description: '温和、福气、享受' },
|
||||
[MajorStar.LIANCHEN]: { name: '廉贞', index: 6, nature: StarNature.XIONG, alias: '情星', description: '感情、才华、是非' },
|
||||
[MajorStar.TIANFU]: { name: '天府', index: 7, nature: StarNature.JI, alias: '库星', description: '稳重、保守、财富' },
|
||||
[MajorStar.TAIYIN]: { name: '太阴', index: 8, nature: StarNature.ZHONGHE, alias: '母星', description: '温柔、内敛、母性' },
|
||||
[MajorStar.TANLANG]: { name: '贪狼', index: 9, nature: StarNature.XIONG, alias: '欲星', description: '欲望、魅力、桃花' },
|
||||
[MajorStar.JUMEN]: { name: '巨门', index: 10, nature: StarNature.XIONG, alias: '口星', description: '口才、是非、沟通' },
|
||||
[MajorStar.TIANXIANG]: { name: '天相', index: 11, nature: StarNature.JI, alias: '印星', description: '辅佐、稳重、贵人' },
|
||||
[MajorStar.TIANLIANG]: { name: '天梁', index: 12, nature: StarNature.ZHONGHE, alias: '荫星', description: '恩泽、稳重、长辈' },
|
||||
[MajorStar.QISHA]: { name: '七杀', index: 13, nature: StarNature.XIONG, alias: '将星', description: '威猛、果断、开创' },
|
||||
[MajorStar.POJUN]: { name: '破军', index: 14, nature: StarNature.XIONG, alias: '耗星', description: '破旧立新、变动、消耗' },
|
||||
}
|
||||
|
||||
const VALUES: MajorStar[] = Object.values(MajorStar).filter(
|
||||
(v): v is MajorStar => typeof v === 'string',
|
||||
)
|
||||
|
||||
export const name = (s: MajorStar): string => DATA[s].name
|
||||
export const index = (s: MajorStar): number => DATA[s].index
|
||||
export const nature = (s: MajorStar): StarNature => DATA[s].nature
|
||||
export const alias = (s: MajorStar): string => DATA[s].alias
|
||||
export const description = (s: MajorStar): string => DATA[s].description
|
||||
|
||||
export function values(): MajorStar[] {
|
||||
return [...VALUES]
|
||||
}
|
||||
}
|
||||
|
||||
export enum PalaceType {
|
||||
MING = 'MING',
|
||||
XIONG = 'XIONG',
|
||||
FUQI = 'FUQI',
|
||||
ZINV = 'ZINV',
|
||||
CAI = 'CAI',
|
||||
JILU = 'JILU',
|
||||
QIAN = 'QIAN',
|
||||
GUANLU = 'GUANLU',
|
||||
TUDI = 'TUDI',
|
||||
FUBEN = 'FUBEN',
|
||||
FU = 'FU',
|
||||
SHEN = 'SHEN',
|
||||
}
|
||||
|
||||
export namespace PalaceType {
|
||||
const DATA: Record<PalaceType, { name: string }> = {
|
||||
[PalaceType.MING]: { name: '命宫' },
|
||||
[PalaceType.XIONG]: { name: '兄弟宫' },
|
||||
[PalaceType.FUQI]: { name: '夫妻宫' },
|
||||
[PalaceType.ZINV]: { name: '子女宫' },
|
||||
[PalaceType.CAI]: { name: '财帛宫' },
|
||||
[PalaceType.JILU]: { name: '疾厄宫' },
|
||||
[PalaceType.QIAN]: { name: '迁移宫' },
|
||||
[PalaceType.GUANLU]: { name: '官禄宫' },
|
||||
[PalaceType.TUDI]: { name: '田宅宫' },
|
||||
[PalaceType.FUBEN]: { name: '福德宫' },
|
||||
[PalaceType.FU]: { name: '父母宫' },
|
||||
[PalaceType.SHEN]: { name: '身宫' },
|
||||
}
|
||||
|
||||
const VALUES: PalaceType[] = Object.values(PalaceType).filter(
|
||||
(v): v is PalaceType => typeof v === 'string',
|
||||
)
|
||||
|
||||
export const name = (s: PalaceType): string => DATA[s].name
|
||||
|
||||
export function values(): PalaceType[] {
|
||||
return [...VALUES]
|
||||
}
|
||||
}
|
||||
|
||||
export enum FortuneType {
|
||||
DAILY = 'DAILY',
|
||||
MONTHLY = 'MONTHLY',
|
||||
YEARLY = 'YEARLY',
|
||||
CAREER = 'CAREER',
|
||||
WEALTH = 'WEALTH',
|
||||
LOVE = 'LOVE',
|
||||
HEALTH = 'HEALTH',
|
||||
STUDY = 'STUDY',
|
||||
SOCIAL = 'SOCIAL',
|
||||
TRAVEL = 'TRAVEL',
|
||||
}
|
||||
|
||||
export enum MinorStarType {
|
||||
LUCKY = 'LUCKY',
|
||||
EVIL = 'EVIL',
|
||||
OTHER = 'OTHER',
|
||||
}
|
||||
|
||||
export namespace MinorStarType {
|
||||
const DATA: Record<MinorStarType, { description: string }> = {
|
||||
[MinorStarType.LUCKY]: { description: '吉星' },
|
||||
[MinorStarType.EVIL]: { description: '煞星' },
|
||||
[MinorStarType.OTHER]: { description: '其他' },
|
||||
}
|
||||
|
||||
export const description = (s: MinorStarType): string => DATA[s].description
|
||||
}
|
||||
|
||||
export enum MinorStar {
|
||||
ZUOFU = 'ZUOFU',
|
||||
YOUBI = 'YOUBI',
|
||||
TIANKUI = 'TIANKUI',
|
||||
TIANYUE = 'TIANYUE',
|
||||
WENCHANG = 'WENCHANG',
|
||||
WENQU = 'WENQU',
|
||||
QINGYANG = 'QINGYANG',
|
||||
TUOLUO = 'TUOLUO',
|
||||
HUOXING = 'HUOXING',
|
||||
LINGXING = 'LINGXING',
|
||||
DIKONG = 'DIKONG',
|
||||
DIJIE = 'DIJIE',
|
||||
TIANXING = 'TIANXING',
|
||||
LUCUN = 'LUCUN',
|
||||
TIANMA = 'TIANMA',
|
||||
HUAGAI = 'HUAGAI',
|
||||
LONGCHI = 'LONGCHI',
|
||||
FENGGU = 'FENGGU',
|
||||
HONGLUAN = 'HONGLUAN',
|
||||
TIANXI = 'TIANXI',
|
||||
GUCHEN = 'GUCHEN',
|
||||
GUAKU = 'GUAKU',
|
||||
}
|
||||
|
||||
export namespace MinorStar {
|
||||
const DATA: Record<
|
||||
MinorStar,
|
||||
{ name: string; type: MinorStarType; alias: string; description: string }
|
||||
> = {
|
||||
[MinorStar.ZUOFU]: { name: '左辅', type: MinorStarType.LUCKY, alias: '左辅星', description: '主辅佐、助力、贵人' },
|
||||
[MinorStar.YOUBI]: { name: '右弼', type: MinorStarType.LUCKY, alias: '右弼星', description: '主辅佐、助力、贵人' },
|
||||
[MinorStar.TIANKUI]: { name: '天魁', type: MinorStarType.LUCKY, alias: '天魁星', description: '主贵人、助力、机遇' },
|
||||
[MinorStar.TIANYUE]: { name: '天钺', type: MinorStarType.LUCKY, alias: '天钺星', description: '主贵人、助力、机遇' },
|
||||
[MinorStar.WENCHANG]: { name: '文昌', type: MinorStarType.LUCKY, alias: '文昌星', description: '主科名、文采、学业' },
|
||||
[MinorStar.WENQU]: { name: '文曲', type: MinorStarType.LUCKY, alias: '文曲星', description: '主口才、艺术、才华' },
|
||||
[MinorStar.QINGYANG]: { name: '擎羊', type: MinorStarType.EVIL, alias: '羊刃星', description: '主刑伤、是非、血光' },
|
||||
[MinorStar.TUOLUO]: { name: '陀罗', type: MinorStarType.EVIL, alias: '陀罗星', description: '主拖延、迟滞、纠结' },
|
||||
[MinorStar.HUOXING]: { name: '火星', type: MinorStarType.EVIL, alias: '火星', description: '主急躁、突发、灾难' },
|
||||
[MinorStar.LINGXING]: { name: '铃星', type: MinorStarType.EVIL, alias: '铃星', description: '主突发、灾难、变动' },
|
||||
[MinorStar.DIKONG]: { name: '地空', type: MinorStarType.EVIL, alias: '地空星', description: '主空亡、损失、虚耗' },
|
||||
[MinorStar.DIJIE]: { name: '地劫', type: MinorStarType.EVIL, alias: '地劫星', description: '主劫夺、损失、破财' },
|
||||
[MinorStar.TIANXING]: { name: '天刑', type: MinorStarType.EVIL, alias: '天刑星', description: '主刑伤、官非、诉讼' },
|
||||
[MinorStar.LUCUN]: { name: '禄存', type: MinorStarType.OTHER, alias: '禄存星', description: '主财帛、福气' },
|
||||
[MinorStar.TIANMA]: { name: '天马', type: MinorStarType.OTHER, alias: '天马星', description: '主迁移、奔波、变动' },
|
||||
[MinorStar.HUAGAI]: { name: '华盖', type: MinorStarType.OTHER, alias: '华盖星', description: '主孤独、艺术、宗教' },
|
||||
[MinorStar.LONGCHI]: { name: '龙池', type: MinorStarType.OTHER, alias: '龙池星', description: '主才华、艺术、文采' },
|
||||
[MinorStar.FENGGU]: { name: '凤阁', type: MinorStarType.OTHER, alias: '凤阁星', description: '主才华、艺术、文采' },
|
||||
[MinorStar.HONGLUAN]: { name: '红鸾', type: MinorStarType.OTHER, alias: '红鸾星', description: '主桃花、婚姻、感情' },
|
||||
[MinorStar.TIANXI]: { name: '天喜', type: MinorStarType.OTHER, alias: '天喜星', description: '主桃花、婚姻、感情' },
|
||||
[MinorStar.GUCHEN]: { name: '孤辰', type: MinorStarType.OTHER, alias: '孤辰星', description: '主孤独、寂寞、寡居' },
|
||||
[MinorStar.GUAKU]: { name: '寡宿', type: MinorStarType.OTHER, alias: '寡宿星', description: '主孤独、寂寞、寡居' },
|
||||
}
|
||||
|
||||
const VALUES: MinorStar[] = Object.values(MinorStar).filter(
|
||||
(v): v is MinorStar => typeof v === 'string',
|
||||
)
|
||||
|
||||
export const name = (s: MinorStar): string => DATA[s].name
|
||||
export const type = (s: MinorStar): MinorStarType => DATA[s].type
|
||||
export const alias = (s: MinorStar): string => DATA[s].alias
|
||||
export const description = (s: MinorStar): string => DATA[s].description
|
||||
|
||||
export function values(): MinorStar[] {
|
||||
return [...VALUES]
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,347 @@
|
||||
import { PalaceType, StarNature, TransformationType } from './enums'
|
||||
import { calculatePalaceScore, determineLuckLevel } from './ziweiAlgorithm'
|
||||
import type { ZiweiChart, Palace, PalaceFortune, DailyFortune, MonthlyFortune, YearlyFortune } from './types'
|
||||
|
||||
export function analyzePalaceFortune(palace: Palace, fortuneDate: Date): PalaceFortune {
|
||||
const baseScore = palace.score
|
||||
const adjustment = calculateDailyAdjustment(palace, fortuneDate)
|
||||
const finalScore = Math.max(0, Math.min(100, baseScore + adjustment))
|
||||
|
||||
return {
|
||||
palaceType: palace.palaceType,
|
||||
score: finalScore,
|
||||
luckLevel: determineLuckLevel(finalScore),
|
||||
analysis: generatePalaceAnalysis(palace, finalScore),
|
||||
advice: generatePalaceAdvice(palace.palaceType, finalScore),
|
||||
}
|
||||
}
|
||||
|
||||
export function calculateDailyAdjustment(palace: Palace, fortuneDate: Date): number {
|
||||
let adjustment = 0
|
||||
const dayOfMonth = fortuneDate.getDate()
|
||||
const month = fortuneDate.getMonth() + 1
|
||||
|
||||
for (const starInfo of palace.majorStars) {
|
||||
if (starInfo.transformation != null) {
|
||||
adjustment += calculateTransformationAdjustment(starInfo.transformation, dayOfMonth)
|
||||
}
|
||||
|
||||
if (starInfo.nature === StarNature.JI) {
|
||||
adjustment += (month === 1 || month === 4 || month === 7 || month === 10) ? 3 : 1
|
||||
} else if (starInfo.nature === StarNature.XIONG) {
|
||||
adjustment -= (month === 2 || month === 5 || month === 8 || month === 11) ? 3 : 1
|
||||
}
|
||||
}
|
||||
|
||||
return adjustment
|
||||
}
|
||||
|
||||
export function calculateMonthlyAdjustment(palace: Palace, month: number): number {
|
||||
let adjustment = 0
|
||||
|
||||
for (const starInfo of palace.majorStars) {
|
||||
if (starInfo.transformation != null) {
|
||||
adjustment += calculateMonthlyTransformationAdjustment(starInfo.transformation, month)
|
||||
}
|
||||
|
||||
if (starInfo.nature === StarNature.JI) {
|
||||
adjustment += (month % 3 === 1) ? 4 : 2
|
||||
} else if (starInfo.nature === StarNature.XIONG) {
|
||||
adjustment -= (month % 3 === 2) ? 4 : 2
|
||||
}
|
||||
}
|
||||
|
||||
return adjustment
|
||||
}
|
||||
|
||||
export function calculateYearlyAdjustment(palace: Palace, year: number): number {
|
||||
let adjustment = 0
|
||||
|
||||
for (const starInfo of palace.majorStars) {
|
||||
if (starInfo.transformation != null) {
|
||||
adjustment += calculateYearlyTransformationAdjustment(starInfo.transformation, year)
|
||||
}
|
||||
|
||||
if (starInfo.nature === StarNature.JI) {
|
||||
adjustment += 3
|
||||
} else if (starInfo.nature === StarNature.XIONG) {
|
||||
adjustment -= 3
|
||||
}
|
||||
}
|
||||
|
||||
return adjustment
|
||||
}
|
||||
|
||||
export function calculateTransformationAdjustment(transformation: TransformationType, dayOfMonth: number): number {
|
||||
switch (transformation) {
|
||||
case TransformationType.LU:
|
||||
return (dayOfMonth % 3 === 0) ? 5 : 2
|
||||
case TransformationType.QUAN:
|
||||
return (dayOfMonth % 7 === 0) ? 5 : 2
|
||||
case TransformationType.KE:
|
||||
return (dayOfMonth % 5 === 0) ? 3 : 1
|
||||
case TransformationType.JI:
|
||||
return (dayOfMonth % 4 === 0) ? -8 : -3
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
export function calculateMonthlyTransformationAdjustment(transformation: TransformationType, month: number): number {
|
||||
switch (transformation) {
|
||||
case TransformationType.LU:
|
||||
return (month % 3 === 1) ? 6 : 3
|
||||
case TransformationType.QUAN:
|
||||
return (month % 3 === 2) ? 6 : 3
|
||||
case TransformationType.KE:
|
||||
return (month % 2 === 0) ? 4 : 2
|
||||
case TransformationType.JI:
|
||||
return (month % 4 === 0) ? -10 : -5
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
export function calculateYearlyTransformationAdjustment(transformation: TransformationType, year: number): number {
|
||||
switch (transformation) {
|
||||
case TransformationType.LU:
|
||||
return (year % 3 === 0) ? 8 : 4
|
||||
case TransformationType.QUAN:
|
||||
return (year % 3 === 1) ? 8 : 4
|
||||
case TransformationType.KE:
|
||||
return (year % 2 === 0) ? 5 : 3
|
||||
case TransformationType.JI:
|
||||
return (year % 4 === 0) ? -12 : -6
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
export function generatePalaceAnalysis(palace: Palace, score: number): string {
|
||||
const parts: string[] = []
|
||||
parts.push(`${PalaceType.name(palace.palaceType)}位于${palace.earthlyBranch}宫`)
|
||||
|
||||
if (palace.majorStars.length > 0) {
|
||||
parts.push(',主星配置:')
|
||||
for (const starInfo of palace.majorStars) {
|
||||
parts.push(starInfo.star)
|
||||
if (starInfo.transformation != null) {
|
||||
parts.push(`(${TransformationType.name(starInfo.transformation)})`)
|
||||
}
|
||||
parts.push(' ')
|
||||
}
|
||||
}
|
||||
|
||||
parts.push(`。运势评分:${score}分`)
|
||||
return parts.join('')
|
||||
}
|
||||
|
||||
export function generatePalaceAdvice(palaceType: PalaceType, score: number): string {
|
||||
if (score >= 70) {
|
||||
return getPositiveAdvice(palaceType)
|
||||
} else if (score >= 50) {
|
||||
return getNeutralAdvice(palaceType)
|
||||
} else {
|
||||
return getNegativeAdvice(palaceType)
|
||||
}
|
||||
}
|
||||
|
||||
function getPositiveAdvice(palaceType: PalaceType): string {
|
||||
const adviceMap: Record<string, string> = {
|
||||
[PalaceType.MING]: '今日状态极佳,适合开展新计划',
|
||||
[PalaceType.XIONG]: '与朋友相处融洽,可拓展人脉',
|
||||
[PalaceType.FUQI]: '感情运势良好,适合表达爱意',
|
||||
[PalaceType.ZINV]: '与子女关系和谐,可增进感情',
|
||||
[PalaceType.CAI]: '财运亨通,可进行投资理财',
|
||||
[PalaceType.JILU]: '身体健康,精力充沛',
|
||||
[PalaceType.QIAN]: '出行顺利,可安排外出计划',
|
||||
[PalaceType.GUANLU]: '工作顺利,可争取更多机会',
|
||||
[PalaceType.TUDI]: '事业运佳,适合拓展业务',
|
||||
[PalaceType.FUBEN]: '贵人运旺,可获得他人帮助',
|
||||
[PalaceType.SHEN]: '心情愉悦,适合社交活动',
|
||||
[PalaceType.FU]: '与父母关系和谐,可多沟通',
|
||||
}
|
||||
return adviceMap[palaceType] ?? '运势良好,保持积极心态'
|
||||
}
|
||||
|
||||
function getNeutralAdvice(palaceType: PalaceType): string {
|
||||
const adviceMap: Record<string, string> = {
|
||||
[PalaceType.MING]: '状态平稳,按计划行事即可',
|
||||
[PalaceType.XIONG]: '与朋友关系一般,保持正常交往',
|
||||
[PalaceType.FUQI]: '感情平稳,多关心对方',
|
||||
[PalaceType.ZINV]: '与子女关系正常,保持沟通',
|
||||
[PalaceType.CAI]: '财运平稳,谨慎理财',
|
||||
[PalaceType.JILU]: '身体状况一般,注意休息',
|
||||
[PalaceType.QIAN]: '出行运势一般,谨慎安排',
|
||||
[PalaceType.GUANLU]: '工作平稳,做好本职工作',
|
||||
[PalaceType.TUDI]: '事业运一般,稳扎稳打',
|
||||
[PalaceType.FUBEN]: '贵人运一般,依靠自身努力',
|
||||
[PalaceType.SHEN]: '心情平稳,保持平常心',
|
||||
[PalaceType.FU]: '与父母关系正常,保持联系',
|
||||
}
|
||||
return adviceMap[palaceType] ?? '运势平稳,保持现状'
|
||||
}
|
||||
|
||||
function getNegativeAdvice(palaceType: PalaceType): string {
|
||||
const adviceMap: Record<string, string> = {
|
||||
[PalaceType.MING]: '状态欠佳,避免重要决策',
|
||||
[PalaceType.XIONG]: '与朋友关系紧张,避免冲突',
|
||||
[PalaceType.FUQI]: '感情运势欠佳,多包容理解',
|
||||
[PalaceType.ZINV]: '与子女关系紧张,耐心沟通',
|
||||
[PalaceType.CAI]: '财运不佳,避免大额投资',
|
||||
[PalaceType.JILU]: '身体欠佳,注意保养',
|
||||
[PalaceType.QIAN]: '出行不利,减少外出',
|
||||
[PalaceType.GUANLU]: '工作压力大,调整心态',
|
||||
[PalaceType.TUDI]: '事业运不佳,谨慎行事',
|
||||
[PalaceType.FUBEN]: '贵人运弱,依靠自己',
|
||||
[PalaceType.SHEN]: '心情不佳,调节情绪',
|
||||
[PalaceType.FU]: '与父母关系紧张,多理解包容',
|
||||
}
|
||||
return adviceMap[palaceType] ?? '运势欠佳,谨慎行事'
|
||||
}
|
||||
|
||||
export function calculateOverallScore(palaceFortunes: Map<string, PalaceFortune> | Record<string, PalaceFortune>): number {
|
||||
const values = palaceFortunes instanceof Map
|
||||
? [...palaceFortunes.values()]
|
||||
: Object.values(palaceFortunes)
|
||||
|
||||
if (values.length === 0) return 60
|
||||
|
||||
const totalScore = values.reduce((sum, pf) => sum + pf.score, 0)
|
||||
return Math.floor(totalScore / values.length)
|
||||
}
|
||||
|
||||
export function generateDailyFortune(chart: ZiweiChart, date: Date): DailyFortune {
|
||||
const palaceFortunes: Record<string, PalaceFortune> = {}
|
||||
|
||||
for (const palace of chart.palaces) {
|
||||
palaceFortunes[palace.palaceType] = analyzePalaceFortune(palace, date)
|
||||
}
|
||||
|
||||
const overallScore = calculateOverallScore(palaceFortunes)
|
||||
|
||||
return {
|
||||
fortuneDate: date.toISOString().slice(0, 10),
|
||||
palaceFortunes,
|
||||
overallScore,
|
||||
overallLuck: determineLuckLevel(overallScore),
|
||||
careerAdvice: generateDailyCareerAdvice(chart, date),
|
||||
wealthAdvice: generateDailyWealthAdvice(chart, date),
|
||||
relationshipAdvice: generateDailyRelationshipAdvice(chart, date),
|
||||
healthAdvice: generateDailyHealthAdvice(chart, date),
|
||||
luckyColor: '',
|
||||
luckyNumber: '',
|
||||
luckyDirection: '',
|
||||
}
|
||||
}
|
||||
|
||||
function generateDailyCareerAdvice(chart: ZiweiChart, date: Date): string {
|
||||
const careerPalace = chart.palaces.find(p => p.palaceType === PalaceType.GUANLU)
|
||||
if (careerPalace == null) return '事业运势不明,谨慎行事'
|
||||
if (careerPalace.score >= 70) {
|
||||
return '今日事业运势吉显,适合处理重要工作事务,可主动争取机会,宜把握良机,必有收获'
|
||||
} else if (careerPalace.score >= 60) {
|
||||
return '今日事业运势平吉,按计划完成工作即可,避免冒险决策,宜稳中求进,循序渐进'
|
||||
} else {
|
||||
return '今日事业运势欠佳,建议处理常规工作,避免重要决策和冲突,宜趋吉避凶,待时而动'
|
||||
}
|
||||
}
|
||||
|
||||
function generateDailyWealthAdvice(chart: ZiweiChart, date: Date): string {
|
||||
const wealthPalace = chart.palaces.find(p => p.palaceType === PalaceType.CAI)
|
||||
if (wealthPalace == null) return '财运不明,谨慎理财'
|
||||
if (wealthPalace.score >= 70) {
|
||||
return '今日财运吉显,适合进行理财规划,可考虑小额投资,宜把握良机,必有收获'
|
||||
} else if (wealthPalace.score >= 60) {
|
||||
return '今日财运平吉,建议保持理性消费,谨慎理财,宜稳中求进,循序渐进'
|
||||
} else {
|
||||
return '今日财运欠佳,建议避免大额支出和投资,保守理财,宜趋吉避凶,待时而动'
|
||||
}
|
||||
}
|
||||
|
||||
function generateDailyRelationshipAdvice(chart: ZiweiChart, date: Date): string {
|
||||
const relationshipPalace = chart.palaces.find(p => p.palaceType === PalaceType.FUQI)
|
||||
if (relationshipPalace == null) return '感情运势不明,保持平常心'
|
||||
if (relationshipPalace.score >= 70) {
|
||||
return '今日感情运势吉显,适合表达爱意,增进感情交流,宜把握良机,必有收获'
|
||||
} else if (relationshipPalace.score >= 60) {
|
||||
return '今日感情运势平吉,保持正常沟通,多关心对方,宜稳中求进,循序渐进'
|
||||
} else {
|
||||
return '今日感情运势欠佳,避免争吵,多包容理解对方,宜趋吉避凶,待时而动'
|
||||
}
|
||||
}
|
||||
|
||||
function generateDailyHealthAdvice(chart: ZiweiChart, date: Date): string {
|
||||
const healthPalace = chart.palaces.find(p => p.palaceType === PalaceType.JILU)
|
||||
if (healthPalace == null) return '健康运势不明,注意休息'
|
||||
if (healthPalace.score >= 70) {
|
||||
return '今日身体状况吉显,精力充沛,可进行适度运动,宜保持良好习惯,适度锻炼'
|
||||
} else if (healthPalace.score >= 60) {
|
||||
return '今日身体状况平吉,注意劳逸结合,保持规律作息,宜稳中求进,循序渐进'
|
||||
} else {
|
||||
return '今日身体状况欠佳,注意休息,避免剧烈运动和熬夜,宜修身养性,待时而动'
|
||||
}
|
||||
}
|
||||
|
||||
export function generateMonthlyFortune(chart: ZiweiChart, year: number, month: number): MonthlyFortune {
|
||||
const palaceFortunes: Record<string, PalaceFortune> = {}
|
||||
|
||||
for (const palace of chart.palaces) {
|
||||
const baseScore = palace.score
|
||||
const adjustment = calculateMonthlyAdjustment(palace, month)
|
||||
const finalScore = Math.max(0, Math.min(100, baseScore + adjustment))
|
||||
|
||||
palaceFortunes[palace.palaceType] = {
|
||||
palaceType: palace.palaceType,
|
||||
score: finalScore,
|
||||
luckLevel: determineLuckLevel(finalScore),
|
||||
analysis: generatePalaceAnalysis(palace, finalScore),
|
||||
advice: generatePalaceAdvice(palace.palaceType, finalScore),
|
||||
}
|
||||
}
|
||||
|
||||
const overallScore = calculateOverallScore(palaceFortunes)
|
||||
|
||||
return {
|
||||
year,
|
||||
month,
|
||||
palaceFortunes,
|
||||
overallScore,
|
||||
overallLuck: determineLuckLevel(overallScore),
|
||||
careerAdvice: '',
|
||||
wealthAdvice: '',
|
||||
relationshipAdvice: '',
|
||||
healthAdvice: '',
|
||||
}
|
||||
}
|
||||
|
||||
export function generateYearlyFortune(chart: ZiweiChart, year: number): YearlyFortune {
|
||||
const palaceFortunes: Record<string, PalaceFortune> = {}
|
||||
|
||||
for (const palace of chart.palaces) {
|
||||
const baseScore = palace.score
|
||||
const adjustment = calculateYearlyAdjustment(palace, year)
|
||||
const finalScore = Math.max(0, Math.min(100, baseScore + adjustment))
|
||||
|
||||
palaceFortunes[palace.palaceType] = {
|
||||
palaceType: palace.palaceType,
|
||||
score: finalScore,
|
||||
luckLevel: determineLuckLevel(finalScore),
|
||||
analysis: generatePalaceAnalysis(palace, finalScore),
|
||||
advice: generatePalaceAdvice(palace.palaceType, finalScore),
|
||||
}
|
||||
}
|
||||
|
||||
const overallScore = calculateOverallScore(palaceFortunes)
|
||||
|
||||
return {
|
||||
year,
|
||||
palaceFortunes,
|
||||
overallScore,
|
||||
overallLuck: determineLuckLevel(overallScore),
|
||||
careerAdvice: '',
|
||||
wealthAdvice: '',
|
||||
relationshipAdvice: '',
|
||||
healthAdvice: '',
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
// @everything-suitable/algorithm 入口
|
||||
// 导出所有纯函数算法,供小程序和云函数共用
|
||||
|
||||
// 类型导出
|
||||
export type {
|
||||
ZiweiChart,
|
||||
Palace,
|
||||
StarInfo,
|
||||
PalaceFortune,
|
||||
DailyFortune,
|
||||
MonthlyFortune,
|
||||
YearlyFortune,
|
||||
LoveFortuneDetail,
|
||||
StudyFortuneDetail,
|
||||
SocialFortuneDetail,
|
||||
TravelFortuneDetail,
|
||||
EmotionalFortuneDetail,
|
||||
FamilyFortuneDetail,
|
||||
WealthFortuneDetail,
|
||||
CareerFortuneDetail,
|
||||
HealthFortuneDetail,
|
||||
SuitableAvoidItem,
|
||||
DailyQuote,
|
||||
ConflictWarning,
|
||||
TimeSlotFortune,
|
||||
StarRating,
|
||||
BirthInfoInput,
|
||||
BirthInfo,
|
||||
SanFangSiZheng,
|
||||
Pattern,
|
||||
} from './types'
|
||||
|
||||
// 枚举导出
|
||||
export {
|
||||
HeavenlyStem,
|
||||
EarthlyBranch,
|
||||
StarNature,
|
||||
TransformationType,
|
||||
MajorStar,
|
||||
PalaceType,
|
||||
MinorStar,
|
||||
MinorStarType,
|
||||
FortuneType,
|
||||
} from './enums'
|
||||
|
||||
// 紫微算法基础
|
||||
export {
|
||||
calculatePalaceScore,
|
||||
calculateLuckyColor,
|
||||
calculateLuckyNumber,
|
||||
calculateLuckyDirection,
|
||||
determineLuckLevel,
|
||||
calculateStarScore,
|
||||
getStarNature,
|
||||
} from './ziweiAlgorithm'
|
||||
|
||||
// 运势策略
|
||||
export {
|
||||
generateDailyFortune,
|
||||
generateMonthlyFortune,
|
||||
generateYearlyFortune,
|
||||
analyzePalaceFortune,
|
||||
calculateDailyAdjustment,
|
||||
calculateMonthlyAdjustment,
|
||||
calculateYearlyAdjustment,
|
||||
calculateOverallScore,
|
||||
} from './fortuneStrategy'
|
||||
|
||||
// 综合运势
|
||||
export {
|
||||
generateComprehensiveFortune,
|
||||
generateComprehensiveMonthlyFortune,
|
||||
generateComprehensiveYearlyFortune,
|
||||
analyzeLoveFortune,
|
||||
analyzeStudyFortune,
|
||||
analyzeSocialFortune,
|
||||
analyzeTravelFortune,
|
||||
analyzeEmotionalFortune,
|
||||
analyzeFamilyFortune,
|
||||
analyzeWealthFortune,
|
||||
analyzeCareerFortune,
|
||||
analyzeHealthFortune,
|
||||
generateDailyQuote,
|
||||
generateSuitableAvoid,
|
||||
generateConflictWarning,
|
||||
generateTimeSlotFortune,
|
||||
generateStarRating,
|
||||
} from './fortune'
|
||||
@@ -0,0 +1,530 @@
|
||||
import type {
|
||||
HeavenlyStem,
|
||||
EarthlyBranch,
|
||||
MajorStar,
|
||||
StarNature,
|
||||
TransformationType,
|
||||
PalaceType,
|
||||
} from './enums'
|
||||
|
||||
export interface BirthInfoInput {
|
||||
birthDate: string
|
||||
birthHour: number
|
||||
birthMinute: number
|
||||
birthPlace: string
|
||||
longitude: number
|
||||
latitude: number
|
||||
gender: 'MALE' | 'FEMALE'
|
||||
}
|
||||
|
||||
export interface LunarDate {
|
||||
lunarYear: string
|
||||
lunarMonth: string
|
||||
lunarDay: string
|
||||
isLeapMonth: boolean
|
||||
zodiac: string
|
||||
solarTerm: string
|
||||
weekday: string
|
||||
}
|
||||
|
||||
export interface BirthInfo {
|
||||
birthTime: string
|
||||
yearStem: HeavenlyStem
|
||||
monthStem: HeavenlyStem
|
||||
dayStem: HeavenlyStem
|
||||
hourStem: HeavenlyStem
|
||||
yearBranch: EarthlyBranch
|
||||
monthBranch: EarthlyBranch
|
||||
dayBranch: EarthlyBranch
|
||||
hourBranch: EarthlyBranch
|
||||
gender: 'MALE' | 'FEMALE'
|
||||
timezone: string
|
||||
longitude?: number
|
||||
latitude?: number
|
||||
birthPlace?: string
|
||||
trueSolarTime?: string
|
||||
lunarDate?: LunarDate
|
||||
}
|
||||
|
||||
export interface StarInfo {
|
||||
star: MajorStar
|
||||
nature: StarNature
|
||||
transformation: TransformationType | null
|
||||
brightness: number
|
||||
description: string
|
||||
}
|
||||
|
||||
export interface Palace {
|
||||
palaceType: PalaceType
|
||||
earthlyBranch: EarthlyBranch
|
||||
majorStars: StarInfo[]
|
||||
minorStars: string[]
|
||||
analysis: string
|
||||
score: number
|
||||
}
|
||||
|
||||
export interface SanFangSiZheng {
|
||||
mingGong: Palace
|
||||
caiBoGong: Palace
|
||||
guanLuGong: Palace
|
||||
qianYiGong: Palace
|
||||
sanFangScore: number
|
||||
siZhengScore: number
|
||||
}
|
||||
|
||||
export interface Pattern {
|
||||
name: string
|
||||
description: string
|
||||
type: string
|
||||
}
|
||||
|
||||
export interface ZiweiChart {
|
||||
birthInfo: BirthInfo
|
||||
palaces: Palace[]
|
||||
yearStem: HeavenlyStem
|
||||
mingGongBranch: EarthlyBranch
|
||||
shenGongBranch: EarthlyBranch
|
||||
summary: string
|
||||
overallLuck: string
|
||||
sanFangSiZheng?: SanFangSiZheng
|
||||
patterns?: Pattern[]
|
||||
}
|
||||
|
||||
export interface PalaceFortune {
|
||||
palaceType: PalaceType
|
||||
score: number
|
||||
luckLevel: string
|
||||
analysis: string
|
||||
advice: string
|
||||
}
|
||||
|
||||
export interface LoveFortuneDetail {
|
||||
loveScore: number
|
||||
overallLoveLuck: string
|
||||
peachBlossomIndex: string
|
||||
singleAdvice: string
|
||||
datingAdvice: string
|
||||
marriageAdvice: string
|
||||
bestDatingTime: string
|
||||
}
|
||||
|
||||
export interface StudyFortuneDetail {
|
||||
studyScore: number
|
||||
overallStudyLuck: string
|
||||
focusIndex: string
|
||||
memoryIndex: string
|
||||
studyAdvice: string
|
||||
examLuck: string
|
||||
bestStudyTime: string
|
||||
suitableSubjects: string
|
||||
studyEnvironmentAdvice: string
|
||||
}
|
||||
|
||||
export interface SocialFortuneDetail {
|
||||
socialScore: number
|
||||
overallSocialLuck: string
|
||||
popularityIndex: string
|
||||
communicationAbility: string
|
||||
socialAdvice: string
|
||||
nobleLuck: string
|
||||
bestSocialTime: string
|
||||
suitableOccasion: string
|
||||
interpersonalAdvice: string
|
||||
}
|
||||
|
||||
export interface TravelFortuneDetail {
|
||||
travelScore: number
|
||||
overallTravelLuck: string
|
||||
safetyIndex: string
|
||||
smoothnessIndex: string
|
||||
travelAdvice: string
|
||||
bestTravelTime: string
|
||||
suitableTransport: string
|
||||
precautions: string
|
||||
nobleDirection: string
|
||||
}
|
||||
|
||||
// === 新增维度类型 ===
|
||||
|
||||
export interface EmotionalFortuneDetail {
|
||||
emotionalScore: number
|
||||
overallEmotionalLuck: string
|
||||
moodIndex: string
|
||||
stressLevel: string
|
||||
emotionalAdvice: string
|
||||
relaxationMethod: string
|
||||
bestRelaxationTime: string
|
||||
}
|
||||
|
||||
export interface FamilyFortuneDetail {
|
||||
familyScore: number
|
||||
overallFamilyLuck: string
|
||||
familyHarmony: string
|
||||
parentRelationship: string
|
||||
childRelationship: string
|
||||
familyAdvice: string
|
||||
suitableFamilyActivity: string
|
||||
}
|
||||
|
||||
export interface WealthFortuneDetail {
|
||||
wealthScore: number
|
||||
overallWealthLuck: string
|
||||
mainWealth: string
|
||||
sideWealth: string
|
||||
spendingTendency: string
|
||||
investmentTiming: string
|
||||
wealthAdvice: string
|
||||
}
|
||||
|
||||
export interface CareerFortuneDetail {
|
||||
careerScore: number
|
||||
overallCareerLuck: string
|
||||
colleagueRelation: string
|
||||
leadershipView: string
|
||||
projectDecision: string
|
||||
promotionLuck: string
|
||||
careerAdvice: string
|
||||
}
|
||||
|
||||
export interface HealthFortuneDetail {
|
||||
healthScore: number
|
||||
overallHealthLuck: string
|
||||
dietAdvice: string
|
||||
exerciseAdvice: string
|
||||
restAdvice: string
|
||||
focusArea: string
|
||||
healthAdvice: string
|
||||
}
|
||||
|
||||
export interface SuitableAvoidItem {
|
||||
suitable: string[]
|
||||
avoid: string[]
|
||||
}
|
||||
|
||||
export interface DailyQuote {
|
||||
quote: string
|
||||
source: string
|
||||
}
|
||||
|
||||
export interface ConflictWarning {
|
||||
hasConflict: boolean
|
||||
conflictType: string
|
||||
conflictDetail: string
|
||||
conflictAdvice: string
|
||||
}
|
||||
|
||||
export interface TimeSlotFortune {
|
||||
morning: string
|
||||
afternoon: string
|
||||
evening: string
|
||||
}
|
||||
|
||||
export interface StarRating {
|
||||
overall: number
|
||||
love: number
|
||||
study: number
|
||||
social: number
|
||||
travel: number
|
||||
wealth: number
|
||||
career: number
|
||||
health: number
|
||||
emotional: number
|
||||
family: number
|
||||
}
|
||||
|
||||
export interface DailyFortune {
|
||||
fortuneDate: string
|
||||
overallLuck: string
|
||||
overallScore: number
|
||||
palaceFortunes: Record<string, PalaceFortune>
|
||||
careerAdvice: string
|
||||
wealthAdvice: string
|
||||
relationshipAdvice: string
|
||||
healthAdvice: string
|
||||
luckyColor: string
|
||||
luckyNumber: string
|
||||
luckyDirection: string
|
||||
loveScore?: number
|
||||
overallLoveLuck?: string
|
||||
peachBlossomIndex?: string
|
||||
singleAdvice?: string
|
||||
datingAdvice?: string
|
||||
marriageAdvice?: string
|
||||
bestDatingTime?: string
|
||||
studyScore?: number
|
||||
overallStudyLuck?: string
|
||||
focusIndex?: string
|
||||
memoryIndex?: string
|
||||
studyAdvice?: string
|
||||
examLuck?: string
|
||||
bestStudyTime?: string
|
||||
suitableSubjects?: string
|
||||
studyEnvironmentAdvice?: string
|
||||
socialScore?: number
|
||||
overallSocialLuck?: string
|
||||
popularityIndex?: string
|
||||
communicationAbility?: string
|
||||
socialAdvice?: string
|
||||
nobleLuck?: string
|
||||
bestSocialTime?: string
|
||||
suitableOccasion?: string
|
||||
interpersonalAdvice?: string
|
||||
travelScore?: number
|
||||
overallTravelLuck?: string
|
||||
safetyIndex?: string
|
||||
smoothnessIndex?: string
|
||||
travelAdvice?: string
|
||||
bestTravelTime?: string
|
||||
suitableTransport?: string
|
||||
precautions?: string
|
||||
nobleDirection?: string
|
||||
|
||||
// 新增维度(A)
|
||||
emotionalScore?: number
|
||||
overallEmotionalLuck?: string
|
||||
moodIndex?: string
|
||||
stressLevel?: string
|
||||
emotionalAdvice?: string
|
||||
relaxationMethod?: string
|
||||
bestRelaxationTime?: string
|
||||
familyScore?: number
|
||||
overallFamilyLuck?: string
|
||||
familyHarmony?: string
|
||||
parentRelationship?: string
|
||||
childRelationship?: string
|
||||
familyAdvice?: string
|
||||
suitableFamilyActivity?: string
|
||||
wealthScore?: number
|
||||
overallWealthLuck?: string
|
||||
mainWealth?: string
|
||||
sideWealth?: string
|
||||
spendingTendency?: string
|
||||
investmentTiming?: string
|
||||
careerScore?: number
|
||||
overallCareerLuck?: string
|
||||
colleagueRelation?: string
|
||||
leadershipView?: string
|
||||
projectDecision?: string
|
||||
promotionLuck?: string
|
||||
healthScore?: number
|
||||
overallHealthLuck?: string
|
||||
dietAdvice?: string
|
||||
exerciseAdvice?: string
|
||||
restAdvice?: string
|
||||
focusArea?: string
|
||||
|
||||
// 新增维度(B)
|
||||
dailyQuote?: DailyQuote
|
||||
loveSuitable?: SuitableAvoidItem
|
||||
studySuitable?: SuitableAvoidItem
|
||||
socialSuitable?: SuitableAvoidItem
|
||||
travelSuitable?: SuitableAvoidItem
|
||||
careerSuitable?: SuitableAvoidItem
|
||||
wealthSuitable?: SuitableAvoidItem
|
||||
conflictWarning?: ConflictWarning
|
||||
|
||||
// 新增维度(D)
|
||||
timeSlotFortune?: TimeSlotFortune
|
||||
starRating?: StarRating
|
||||
}
|
||||
|
||||
export interface MonthlyFortune {
|
||||
fortuneMonth: string
|
||||
overallLuck: string
|
||||
overallScore: number
|
||||
palaceFortunes: Record<string, PalaceFortune>
|
||||
dailyFortunes: DailyFortune[]
|
||||
careerAdvice: string
|
||||
wealthAdvice: string
|
||||
relationshipAdvice: string
|
||||
healthAdvice: string
|
||||
luckyColor: string
|
||||
luckyNumber: string
|
||||
luckyDirection: string
|
||||
keyFocus: string
|
||||
cautionAdvice: string
|
||||
loveScore?: number
|
||||
overallLoveLuck?: string
|
||||
peachBlossomIndex?: string
|
||||
singleAdvice?: string
|
||||
datingAdvice?: string
|
||||
marriageAdvice?: string
|
||||
bestDatingTime?: string
|
||||
studyScore?: number
|
||||
overallStudyLuck?: string
|
||||
focusIndex?: string
|
||||
memoryIndex?: string
|
||||
studyAdvice?: string
|
||||
examLuck?: string
|
||||
bestStudyTime?: string
|
||||
suitableSubjects?: string
|
||||
studyEnvironmentAdvice?: string
|
||||
socialScore?: number
|
||||
overallSocialLuck?: string
|
||||
popularityIndex?: string
|
||||
communicationAbility?: string
|
||||
socialAdvice?: string
|
||||
nobleLuck?: string
|
||||
bestSocialTime?: string
|
||||
suitableOccasion?: string
|
||||
interpersonalAdvice?: string
|
||||
travelScore?: number
|
||||
overallTravelLuck?: string
|
||||
safetyIndex?: string
|
||||
smoothnessIndex?: string
|
||||
travelAdvice?: string
|
||||
bestTravelTime?: string
|
||||
suitableTransport?: string
|
||||
precautions?: string
|
||||
nobleDirection?: string
|
||||
|
||||
// 新增维度(A)
|
||||
emotionalScore?: number
|
||||
overallEmotionalLuck?: string
|
||||
moodIndex?: string
|
||||
stressLevel?: string
|
||||
emotionalAdvice?: string
|
||||
relaxationMethod?: string
|
||||
bestRelaxationTime?: string
|
||||
familyScore?: number
|
||||
overallFamilyLuck?: string
|
||||
familyHarmony?: string
|
||||
parentRelationship?: string
|
||||
childRelationship?: string
|
||||
familyAdvice?: string
|
||||
suitableFamilyActivity?: string
|
||||
wealthScore?: number
|
||||
overallWealthLuck?: string
|
||||
mainWealth?: string
|
||||
sideWealth?: string
|
||||
spendingTendency?: string
|
||||
investmentTiming?: string
|
||||
careerScore?: number
|
||||
overallCareerLuck?: string
|
||||
colleagueRelation?: string
|
||||
leadershipView?: string
|
||||
projectDecision?: string
|
||||
promotionLuck?: string
|
||||
healthScore?: number
|
||||
overallHealthLuck?: string
|
||||
dietAdvice?: string
|
||||
exerciseAdvice?: string
|
||||
restAdvice?: string
|
||||
focusArea?: string
|
||||
|
||||
// 新增维度(B)
|
||||
dailyQuote?: DailyQuote
|
||||
loveSuitable?: SuitableAvoidItem
|
||||
studySuitable?: SuitableAvoidItem
|
||||
socialSuitable?: SuitableAvoidItem
|
||||
travelSuitable?: SuitableAvoidItem
|
||||
careerSuitable?: SuitableAvoidItem
|
||||
wealthSuitable?: SuitableAvoidItem
|
||||
conflictWarning?: ConflictWarning
|
||||
|
||||
// 新增维度(D)
|
||||
timeSlotFortune?: TimeSlotFortune
|
||||
starRating?: StarRating
|
||||
}
|
||||
|
||||
export interface YearlyFortune {
|
||||
fortuneYear: number
|
||||
overallLuck: string
|
||||
overallScore: number
|
||||
palaceFortunes: Record<string, PalaceFortune>
|
||||
monthlyFortunes: MonthlyFortune[]
|
||||
careerAdvice: string
|
||||
wealthAdvice: string
|
||||
relationshipAdvice: string
|
||||
healthAdvice: string
|
||||
luckyColor: string
|
||||
luckyNumber: string
|
||||
luckyDirection: string
|
||||
keyFocus: string
|
||||
cautionAdvice: string
|
||||
yearlyTheme: string
|
||||
majorOpportunity: string
|
||||
majorChallenge: string
|
||||
loveScore?: number
|
||||
overallLoveLuck?: string
|
||||
peachBlossomIndex?: string
|
||||
singleAdvice?: string
|
||||
datingAdvice?: string
|
||||
marriageAdvice?: string
|
||||
bestDatingTime?: string
|
||||
studyScore?: number
|
||||
overallStudyLuck?: string
|
||||
focusIndex?: string
|
||||
memoryIndex?: string
|
||||
studyAdvice?: string
|
||||
examLuck?: string
|
||||
bestStudyTime?: string
|
||||
suitableSubjects?: string
|
||||
studyEnvironmentAdvice?: string
|
||||
socialScore?: number
|
||||
overallSocialLuck?: string
|
||||
popularityIndex?: string
|
||||
communicationAbility?: string
|
||||
socialAdvice?: string
|
||||
nobleLuck?: string
|
||||
bestSocialTime?: string
|
||||
suitableOccasion?: string
|
||||
interpersonalAdvice?: string
|
||||
travelScore?: number
|
||||
overallTravelLuck?: string
|
||||
safetyIndex?: string
|
||||
smoothnessIndex?: string
|
||||
travelAdvice?: string
|
||||
bestTravelTime?: string
|
||||
suitableTransport?: string
|
||||
precautions?: string
|
||||
nobleDirection?: string
|
||||
|
||||
// 新增维度(A)
|
||||
emotionalScore?: number
|
||||
overallEmotionalLuck?: string
|
||||
moodIndex?: string
|
||||
stressLevel?: string
|
||||
emotionalAdvice?: string
|
||||
relaxationMethod?: string
|
||||
bestRelaxationTime?: string
|
||||
familyScore?: number
|
||||
overallFamilyLuck?: string
|
||||
familyHarmony?: string
|
||||
parentRelationship?: string
|
||||
childRelationship?: string
|
||||
familyAdvice?: string
|
||||
suitableFamilyActivity?: string
|
||||
wealthScore?: number
|
||||
overallWealthLuck?: string
|
||||
mainWealth?: string
|
||||
sideWealth?: string
|
||||
spendingTendency?: string
|
||||
investmentTiming?: string
|
||||
careerScore?: number
|
||||
overallCareerLuck?: string
|
||||
colleagueRelation?: string
|
||||
leadershipView?: string
|
||||
projectDecision?: string
|
||||
promotionLuck?: string
|
||||
healthScore?: number
|
||||
overallHealthLuck?: string
|
||||
dietAdvice?: string
|
||||
exerciseAdvice?: string
|
||||
restAdvice?: string
|
||||
focusArea?: string
|
||||
|
||||
// 新增维度(B)
|
||||
dailyQuote?: DailyQuote
|
||||
loveSuitable?: SuitableAvoidItem
|
||||
studySuitable?: SuitableAvoidItem
|
||||
socialSuitable?: SuitableAvoidItem
|
||||
travelSuitable?: SuitableAvoidItem
|
||||
careerSuitable?: SuitableAvoidItem
|
||||
wealthSuitable?: SuitableAvoidItem
|
||||
conflictWarning?: ConflictWarning
|
||||
|
||||
// 新增维度(D)
|
||||
timeSlotFortune?: TimeSlotFortune
|
||||
starRating?: StarRating
|
||||
}
|
||||
@@ -0,0 +1,274 @@
|
||||
import { MajorStar, StarNature, TransformationType, EarthlyBranch, PalaceType } from './enums'
|
||||
import type { StarInfo, Palace, ZiweiChart } from './types'
|
||||
|
||||
const STAR_NATURE_MAP = new Map<MajorStar, StarNature>([
|
||||
[MajorStar.ZIWEI, StarNature.JI],
|
||||
[MajorStar.TIANJI, StarNature.JI],
|
||||
[MajorStar.TAIYANG, StarNature.JI],
|
||||
[MajorStar.WUQU, StarNature.XIONG],
|
||||
[MajorStar.TIANTONG, StarNature.JI],
|
||||
[MajorStar.LIANCHEN, StarNature.XIONG],
|
||||
[MajorStar.TIANFU, StarNature.JI],
|
||||
[MajorStar.TAIYIN, StarNature.ZHONGHE],
|
||||
[MajorStar.TANLANG, StarNature.XIONG],
|
||||
[MajorStar.JUMEN, StarNature.XIONG],
|
||||
[MajorStar.TIANXIANG, StarNature.JI],
|
||||
[MajorStar.TIANLIANG, StarNature.ZHONGHE],
|
||||
[MajorStar.QISHA, StarNature.XIONG],
|
||||
[MajorStar.POJUN, StarNature.XIONG],
|
||||
])
|
||||
|
||||
const STAR_BASE_SCORE_MAP = new Map<MajorStar, number>([
|
||||
[MajorStar.ZIWEI, 85],
|
||||
[MajorStar.TIANJI, 75],
|
||||
[MajorStar.TAIYANG, 80],
|
||||
[MajorStar.WUQU, 75],
|
||||
[MajorStar.TIANTONG, 70],
|
||||
[MajorStar.LIANCHEN, 55],
|
||||
[MajorStar.TIANFU, 85],
|
||||
[MajorStar.TAIYIN, 75],
|
||||
[MajorStar.TANLANG, 60],
|
||||
[MajorStar.JUMEN, 55],
|
||||
[MajorStar.TIANXIANG, 75],
|
||||
[MajorStar.TIANLIANG, 75],
|
||||
[MajorStar.QISHA, 60],
|
||||
[MajorStar.POJUN, 55],
|
||||
])
|
||||
|
||||
const TRANSFORMATION_SCORE_MAP = new Map<TransformationType, number>([
|
||||
[TransformationType.LU, 15],
|
||||
[TransformationType.QUAN, 10],
|
||||
[TransformationType.KE, 8],
|
||||
[TransformationType.JI, -15],
|
||||
])
|
||||
|
||||
const BRANCH_COLOR_MAP = new Map<EarthlyBranch, string>([
|
||||
[EarthlyBranch.ZI, '黑色'],
|
||||
[EarthlyBranch.CHOU, '黄色'],
|
||||
[EarthlyBranch.YIN, '绿色'],
|
||||
[EarthlyBranch.MAO, '绿色'],
|
||||
[EarthlyBranch.CHEN, '黄色'],
|
||||
[EarthlyBranch.SI, '红色'],
|
||||
[EarthlyBranch.WU, '红色'],
|
||||
[EarthlyBranch.WEI, '黄色'],
|
||||
[EarthlyBranch.SHEN, '白色'],
|
||||
[EarthlyBranch.YOU, '白色'],
|
||||
[EarthlyBranch.XU, '黄色'],
|
||||
[EarthlyBranch.HAI, '黑色'],
|
||||
])
|
||||
|
||||
const BRANCH_DIRECTION_MAP = new Map<EarthlyBranch, string>([
|
||||
[EarthlyBranch.ZI, '正北'],
|
||||
[EarthlyBranch.CHOU, '东北偏北'],
|
||||
[EarthlyBranch.YIN, '东北偏东'],
|
||||
[EarthlyBranch.MAO, '正东'],
|
||||
[EarthlyBranch.CHEN, '东南偏东'],
|
||||
[EarthlyBranch.SI, '东南偏南'],
|
||||
[EarthlyBranch.WU, '正南'],
|
||||
[EarthlyBranch.WEI, '西南偏南'],
|
||||
[EarthlyBranch.SHEN, '西南偏西'],
|
||||
[EarthlyBranch.YOU, '正西'],
|
||||
[EarthlyBranch.XU, '西北偏西'],
|
||||
[EarthlyBranch.HAI, '西北偏北'],
|
||||
])
|
||||
|
||||
const LUCKY_COLORS = ['红色', '黄色', '蓝色', '绿色', '紫色', '金色', '白色', '黑色']
|
||||
const LUCKY_DIRECTIONS = ['东方', '南方', '西方', '北方', '东南', '西南', '东北', '西北']
|
||||
|
||||
export function getStarNature(star: MajorStar): StarNature {
|
||||
return STAR_NATURE_MAP.get(star) ?? StarNature.ZHONGHE
|
||||
}
|
||||
|
||||
export function calculateStarScore(starInfo: StarInfo | null): number {
|
||||
if (starInfo == null || starInfo.star == null) {
|
||||
return 50
|
||||
}
|
||||
|
||||
const star = starInfo.star
|
||||
let baseScore = STAR_BASE_SCORE_MAP.get(star) ?? 50
|
||||
|
||||
const transformation = starInfo.transformation
|
||||
if (transformation != null) {
|
||||
const transScore = TRANSFORMATION_SCORE_MAP.get(transformation) ?? 0
|
||||
baseScore += transScore
|
||||
}
|
||||
|
||||
const brightness = starInfo.brightness
|
||||
if (brightness > 0 && brightness <= 100) {
|
||||
const brightnessFactor = brightness / 100.0
|
||||
baseScore = Math.floor(baseScore * brightnessFactor)
|
||||
}
|
||||
|
||||
return Math.max(0, Math.min(100, baseScore))
|
||||
}
|
||||
|
||||
export function calculatePalaceScore(palace: Palace | null): number {
|
||||
if (palace == null) {
|
||||
return 50
|
||||
}
|
||||
|
||||
let baseScore = 50
|
||||
const majorStars = palace.majorStars
|
||||
|
||||
if (majorStars == null || majorStars.length === 0) {
|
||||
return baseScore
|
||||
}
|
||||
|
||||
let starScoreSum = 0
|
||||
let starCount = 0
|
||||
|
||||
for (const starInfo of majorStars) {
|
||||
if (starInfo == null || starInfo.star == null) {
|
||||
continue
|
||||
}
|
||||
|
||||
const starScore = calculateStarScore(starInfo)
|
||||
starScoreSum += starScore
|
||||
starCount++
|
||||
}
|
||||
|
||||
if (starCount > 0) {
|
||||
baseScore = Math.floor(starScoreSum / starCount)
|
||||
}
|
||||
|
||||
const finalScore = Math.max(0, Math.min(100, baseScore))
|
||||
palace.score = finalScore
|
||||
|
||||
return finalScore
|
||||
}
|
||||
|
||||
export function calculateLuckyColor(chart: ZiweiChart, fortuneDate: Date): string {
|
||||
if (chart == null || fortuneDate == null) {
|
||||
return '红色'
|
||||
}
|
||||
|
||||
const mingGongBranch = chart.mingGongBranch
|
||||
if (mingGongBranch == null) {
|
||||
return '红色'
|
||||
}
|
||||
|
||||
const baseColor = BRANCH_COLOR_MAP.get(mingGongBranch) ?? '红色'
|
||||
|
||||
const mingGong = chart.palaces.find(p => p.palaceType === PalaceType.MING)
|
||||
if (mingGong == null || mingGong.majorStars.length === 0) {
|
||||
return baseColor
|
||||
}
|
||||
|
||||
const mainStar = mingGong.majorStars[0]
|
||||
const starNature = MajorStar.nature(mainStar.star)
|
||||
|
||||
let baseIndex = 0
|
||||
for (let i = 0; i < LUCKY_COLORS.length; i++) {
|
||||
if (LUCKY_COLORS[i] === baseColor) {
|
||||
baseIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
let natureOffset = 0
|
||||
if (starNature === StarNature.JI) {
|
||||
natureOffset = 1
|
||||
} else if (starNature === StarNature.XIONG) {
|
||||
natureOffset = -1
|
||||
}
|
||||
|
||||
const dayOfMonth = fortuneDate.getDate()
|
||||
const dateOffset = dayOfMonth % 3
|
||||
|
||||
const colorIndex = ((baseIndex + natureOffset + dateOffset) % LUCKY_COLORS.length + LUCKY_COLORS.length) % LUCKY_COLORS.length
|
||||
|
||||
return LUCKY_COLORS[colorIndex]
|
||||
}
|
||||
|
||||
export function calculateLuckyNumber(chart: ZiweiChart, fortuneDate: Date): string {
|
||||
if (chart == null || fortuneDate == null) {
|
||||
return '8'
|
||||
}
|
||||
|
||||
const dayOfMonth = fortuneDate.getDate()
|
||||
const monthValue = fortuneDate.getMonth() + 1
|
||||
|
||||
let baseNumber = (dayOfMonth + monthValue) % 9
|
||||
if (baseNumber === 0) {
|
||||
baseNumber = 9
|
||||
}
|
||||
|
||||
const mingGongBranch = chart.mingGongBranch
|
||||
if (mingGongBranch == null) {
|
||||
return String(baseNumber)
|
||||
}
|
||||
|
||||
const branchIndex = EarthlyBranch.index(mingGongBranch)
|
||||
|
||||
const mingGong = chart.palaces.find(p => p.palaceType === PalaceType.MING)
|
||||
let starFactor = 0
|
||||
if (mingGong != null && mingGong.majorStars.length > 0) {
|
||||
const mainStar = mingGong.majorStars[0]
|
||||
const starNature = MajorStar.nature(mainStar.star)
|
||||
if (starNature === StarNature.JI) {
|
||||
starFactor = 1
|
||||
} else if (starNature === StarNature.XIONG) {
|
||||
starFactor = -1
|
||||
}
|
||||
}
|
||||
|
||||
let luckyNumber = (baseNumber + branchIndex + starFactor) % 9
|
||||
if (luckyNumber === 0) {
|
||||
luckyNumber = 9
|
||||
}
|
||||
|
||||
return String(luckyNumber)
|
||||
}
|
||||
|
||||
export function calculateLuckyDirection(chart: ZiweiChart, fortuneDate: Date): string {
|
||||
if (chart == null || fortuneDate == null) {
|
||||
return '东方'
|
||||
}
|
||||
|
||||
const mingGongBranch = chart.mingGongBranch
|
||||
if (mingGongBranch == null) {
|
||||
return '东方'
|
||||
}
|
||||
|
||||
const baseDirection = BRANCH_DIRECTION_MAP.get(mingGongBranch) ?? '东方'
|
||||
|
||||
const mingGong = chart.palaces.find(p => p.palaceType === PalaceType.MING)
|
||||
if (mingGong == null || mingGong.majorStars.length === 0) {
|
||||
return baseDirection
|
||||
}
|
||||
|
||||
const mainStar = mingGong.majorStars[0]
|
||||
const starNature = MajorStar.nature(mainStar.star)
|
||||
|
||||
let baseIndex = 0
|
||||
for (let i = 0; i < LUCKY_DIRECTIONS.length; i++) {
|
||||
if (LUCKY_DIRECTIONS[i] === baseDirection) {
|
||||
baseIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
let natureOffset = 0
|
||||
if (starNature === StarNature.JI) {
|
||||
natureOffset = 1
|
||||
} else if (starNature === StarNature.XIONG) {
|
||||
natureOffset = -1
|
||||
}
|
||||
|
||||
const dayOfMonth = fortuneDate.getDate()
|
||||
const dateOffset = dayOfMonth % 2
|
||||
|
||||
const directionIndex = ((baseIndex + natureOffset + dateOffset) % LUCKY_DIRECTIONS.length + LUCKY_DIRECTIONS.length) % LUCKY_DIRECTIONS.length
|
||||
|
||||
return LUCKY_DIRECTIONS[directionIndex]
|
||||
}
|
||||
|
||||
export function determineLuckLevel(score: number): string {
|
||||
if (score >= 90) return '大吉'
|
||||
if (score >= 80) return '吉'
|
||||
if (score >= 70) return '中吉'
|
||||
if (score >= 60) return '平'
|
||||
if (score >= 50) return '中平'
|
||||
if (score >= 40) return '小凶'
|
||||
return '凶'
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"sourceMap": true,
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src"
|
||||
},
|
||||
"include": ["src/**/*.ts"],
|
||||
"exclude": ["node_modules", "dist", "**/__tests__/**"]
|
||||
}
|
||||
Reference in New Issue
Block a user