import { DynamicReport, StaticReport, SynthesisReport, DimensionScores, RuleEvidence, ScopeSynthesisReport, TimeLayerScope } from './types'; import { CONFLICT_RESOLUTION, BONUS_RULES, PENALTY_RULES } from './scoring-tables'; import { RuleAuditor } from './rule-auditor'; export class SynthesisLayer { private staticReport: StaticReport; private dynamicReport: DynamicReport; private auditor: RuleAuditor; private version = 'synthesis-v2.0.0'; constructor(staticReport: StaticReport, dynamicReport: DynamicReport, auditor: RuleAuditor) { this.staticReport = staticReport; this.dynamicReport = dynamicReport; this.auditor = auditor; } public analyze(): SynthesisReport { const scopes: TimeLayerScope[] = ['original', 'daXian', 'liuNian', 'liuYue', 'liuRi', 'liuShi']; const scopedReports: Record = {}; scopes.forEach(scope => { scopedReports[scope] = this.analyzeScope(scope); }); return { global: scopedReports['original'], scoped: scopedReports, }; } private analyzeScope(scope: TimeLayerScope): ScopeSynthesisReport { // 1. 提取所有规则证据 (这里可以根据scope进行筛选,目前简化为全部) const allEvidences: RuleEvidence[] = this.auditor.getEvidencesByScope(scope); // 2. 基础底分配置 let totalScore = 50; const dimensionScores: DimensionScores = { career: 50, wealth: 50, marriage: 50, health: 50, personality: 50, }; // 3. 星曜状态基础加减分 (透明化权重) totalScore += this.staticReport.starsStatus.miaoWangCount * 2; totalScore -= this.staticReport.starsStatus.pingXianCount * 2; totalScore += this.staticReport.starsStatus.jiXingCount * 3; totalScore -= this.staticReport.starsStatus.shaXingCount * 3; // 4. 累加各个规则的影响分 let rawBonus = 0; let rawPenalty = 0; allEvidences.forEach(ev => { if (ev.scoreImpact > 0) { rawBonus += ev.scoreImpact; } else if (ev.scoreImpact < 0) { rawPenalty += ev.scoreImpact; } this.auditor.collectScoreBreakdown(scope, { ruleId: ev.ruleId, title: ev.title, scoreImpact: ev.scoreImpact, tags: ev.tags, }); // 分项雷达图得分累计 if (ev.tags.includes('事业')) dimensionScores.career += ev.scoreImpact; if (ev.tags.includes('财富')) dimensionScores.wealth += ev.scoreImpact; if (ev.tags.includes('婚姻')) dimensionScores.marriage += ev.scoreImpact; if (ev.tags.includes('健康')) dimensionScores.health += ev.scoreImpact; if (ev.tags.includes('性格')) dimensionScores.personality += ev.scoreImpact; }); // 5. 静动交涉:冲突与增强 const synthesisEvidences: RuleEvidence[] = []; // TODO: Need scope-specific sihua facts, here we use original for simplicity const shengNianJiPalace = this.dynamicReport.sihua.shengNian['忌']; const shengNianLuPalace = this.dynamicReport.sihua.shengNian['禄']; let hasLu = false; let hasJi = false; if (shengNianJiPalace && ['命宫', '财帛宫', '官禄宫', '迁移宫'].includes(shengNianJiPalace)) { hasJi = true; rawPenalty -= 15; // 追加惩罚 if (this.staticReport.patterns.length > 0) { const ev: RuleEvidence = { ruleId: 'ZW_SYN_CONFLICT_JI_BREAK_v1', title: '吉格遇忌冲', type: 'synthesis', description: `静态命盘有成格,但生年忌落入【${shengNianJiPalace}】冲克,主吉处藏凶,成败起伏大。`, boardEvidence: ['静态有格局', `动态生年忌入${shengNianJiPalace}`], scoreImpact: -15, tags: ['风险', '事业', '财富'], version: this.version, scope }; synthesisEvidences.push(ev); this.auditor.collectEvidence(ev); } } if (shengNianLuPalace && ['命宫', '财帛宫', '官禄宫', '迁移宫'].includes(shengNianLuPalace)) { hasLu = true; rawBonus += 15; // 追加奖励 if (this.staticReport.patterns.length > 0) { const ev: RuleEvidence = { ruleId: 'ZW_SYN_REINFORCE_LU_BOOST_v1', title: '吉格逢禄引动', type: 'synthesis', description: `静态成格且生年禄入【${shengNianLuPalace}】,静动态高度一致,大吉,此生必有建树。`, boardEvidence: ['静态有格局', `动态生年禄入${shengNianLuPalace}`], scoreImpact: 15, tags: ['机遇', '事业', '财富'], version: this.version, scope }; synthesisEvidences.push(ev); this.auditor.collectEvidence(ev); } } // 6. 冲突解决逻辑 const hasGoodPattern = this.staticReport.patterns.length > 0; const hasBadInteraction = this.staticReport.interactions.some(e => e.scoreImpact < 0); let finalBonus = rawBonus; let finalPenalty = rawPenalty; // 遍历优先级解决冲突 for (const resolution of CONFLICT_RESOLUTION) { if (resolution.name === '禄忌交战' && resolution.condition(hasLu, hasJi)) { const adj = { resolutionId: 'CONFLICT_LU_JI', description: resolution.action, originalScore: finalBonus, adjustedScore: finalBonus / 2 }; finalBonus = finalBonus / 2; this.auditor.collectConflictAdjustment(scope, adj); const ev: RuleEvidence = { ruleId: 'ZW_SYN_CONFLICT_LU_JI_v1', title: '禄忌交战', type: 'synthesis', description: resolution.action, boardEvidence: ['同时逢生年禄与生年忌入核心宫位'], scoreImpact: 0, // 仅作说明 tags: ['冲突'], version: this.version, scope }; synthesisEvidences.push(ev); this.auditor.collectEvidence(ev); } if (resolution.name === '破格优先' && resolution.condition(hasGoodPattern, hasBadInteraction)) { const adj = { resolutionId: 'CONFLICT_BROKEN_PATTERN', description: resolution.action, originalScore: finalPenalty, adjustedScore: finalPenalty - 10 }; finalPenalty -= 10; // 向下修正 this.auditor.collectConflictAdjustment(scope, adj); const ev: RuleEvidence = { ruleId: 'ZW_SYN_CONFLICT_BROKEN_PATTERN_v1', title: '破格优先', type: 'synthesis', description: resolution.action, boardEvidence: ['静态有吉格,但同时存在凶格夹冲'], scoreImpact: -10, tags: ['冲突', '风险'], version: this.version, scope }; synthesisEvidences.push(ev); this.auditor.collectEvidence(ev); } } totalScore += finalBonus + finalPenalty; // 约束分数范围 const clamp = (val: number) => Math.max(0, Math.min(100, val)); totalScore = clamp(totalScore); dimensionScores.career = clamp(dimensionScores.career); dimensionScores.wealth = clamp(dimensionScores.wealth); dimensionScores.marriage = clamp(dimensionScores.marriage); dimensionScores.health = clamp(dimensionScores.health); dimensionScores.personality = clamp(dimensionScores.personality); // 评估置信度 let confidence: 'High' | 'Medium' | 'Low' = 'Medium'; if (this.staticReport.starsStatus.shaXingCount >= 4 && synthesisEvidences.some(e => e.scoreImpact < 0)) { confidence = 'Low'; // 煞星多且有冲突,命盘波动极大,断语可信度下降 } else if (this.staticReport.starsStatus.miaoWangCount >= 6 && this.staticReport.starsStatus.shaXingCount <= 2) { confidence = 'High'; // 命盘稳定,特征明显 } return { scope, totalScore, dimensionScores, confidence, evidences: synthesisEvidences, scoreBreakdown: this.auditor.getScoreBreakdown(scope), conflictAdjustments: this.auditor.getConflictAdjustments(scope), }; } }