diff --git a/docs/QIMEN_DEVELOPMENT_PLAN.md b/docs/QIMEN_DEVELOPMENT_PLAN.md new file mode 100644 index 0000000..9a33dd7 --- /dev/null +++ b/docs/QIMEN_DEVELOPMENT_PLAN.md @@ -0,0 +1,653 @@ +# 奇门遁甲模块开发计划 + +## 项目概述 + +基于已完成的奇门遁甲理论文档和基础代码框架,开发一个完整的奇门遁甲分析模块,为用户提供专业、准确、易用的奇门遁甲预测服务。 + +## 开发目标 + +### 核心目标 +- 实现完整的奇门遁甲起局算法 +- 提供准确的格局分析和用神判断 +- 生成专业的预测结果和指导建议 +- 支持多种问题类型的智能分析 + +### 技术目标 +- 高性能的计算引擎 +- 可扩展的架构设计 +- 完善的缓存机制 +- 友好的API接口 + +## 技术架构 + +### 后端架构 +``` +奇门遁甲模块 +├── 核心计算引擎 (QimenCalculator) +│ ├── 时间转换模块 +│ ├── 节气计算模块 +│ ├── 起局算法模块 +│ └── 格局识别模块 +├── 分析服务 (QimenAnalyzer) +│ ├── 用神选择模块 +│ ├── 旺衰判断模块 +│ ├── 格局分析模块 +│ └── 预测生成模块 +├── 数据管理 (QimenDataManager) +│ ├── 基础数据管理 +│ ├── 缓存管理 +│ └── 历史记录管理 +└── API接口层 (QimenRoutes) + ├── 起局接口 + ├── 分析接口 + ├── 预测接口 + └── 工具接口 +``` + +### 前端架构 +``` +奇门遁甲前端模块 +├── 页面组件 +│ ├── 奇门起局页面 +│ ├── 分析结果页面 +│ ├── 历史记录页面 +│ └── 学习资料页面 +├── 业务组件 +│ ├── 奇门盘显示组件 +│ ├── 格局分析组件 +│ ├── 预测结果组件 +│ └── 时间选择组件 +├── 工具组件 +│ ├── 九宫格组件 +│ ├── 干支显示组件 +│ ├── 五行图表组件 +│ └── 导出功能组件 +└── 服务层 + ├── 奇门API服务 + ├── 数据格式化服务 + └── 本地存储服务 +``` + +## 功能模块详细设计 + +### 1. 核心计算引擎 + +#### 1.1 时间转换模块 +**功能**:公历转农历、干支纪年法转换 +**输入**:公历日期时间 +**输出**:干支纪年、月、日、时 +**关键算法**: +- 万年历算法 +- 干支循环计算 +- 节气精确计算 + +#### 1.2 节气计算模块 +**功能**:精确计算二十四节气时间 +**输入**:年份 +**输出**:全年节气时间表 +**关键算法**: +- 天文算法计算节气 +- 时区转换处理 +- 历史数据校验 + +#### 1.3 起局算法模块 +**功能**:根据时间信息生成奇门盘 +**输入**:干支时间、节气信息 +**输出**:完整的奇门盘数据 +**关键算法**: +- 阴阳遁判断 +- 局数计算 +- 三奇六仪排布 +- 九星八门八神排布 + +#### 1.4 格局识别模块 +**功能**:识别奇门盘中的各种格局 +**输入**:奇门盘数据 +**输出**:格局列表及评级 +**关键算法**: +- 三奇格局识别 +- 六仪格局识别 +- 特殊格局识别 +- 组合格局分析 + +### 2. 分析服务模块 + +#### 2.1 用神选择模块 +**功能**:根据问题类型智能选择用神 +**输入**:问题描述、用户信息 +**输出**:用神配置 +**关键算法**: +- 问题分类算法 +- 用神匹配规则 +- 优先级排序 + +#### 2.2 旺衰判断模块 +**功能**:判断用神在当前时空的旺衰状态 +**输入**:用神、时间信息 +**输出**:旺衰等级 +**关键算法**: +- 五行生克关系 +- 季节旺衰规律 +- 宫位影响因素 + +#### 2.3 格局分析模块 +**功能**:综合分析格局对事情的影响 +**输入**:格局列表、用神信息 +**输出**:格局分析报告 +**关键算法**: +- 格局权重计算 +- 吉凶综合评估 +- 影响因素分析 + +#### 2.4 预测生成模块 +**功能**:生成最终的预测结果和建议 +**输入**:所有分析结果 +**输出**:预测报告 +**关键算法**: +- 综合评分算法 +- 建议生成规则 +- 应期计算方法 + +### 3. 数据管理模块 + +#### 3.1 基础数据管理 +**功能**:管理奇门遁甲基础数据 +**数据类型**: +- 九星数据 +- 八门数据 +- 八神数据 +- 格局数据 +- 用神配置 + +#### 3.2 缓存管理 +**功能**:提高计算性能 +**缓存策略**: +- 奇门盘缓存(1小时) +- 节气数据缓存(1年) +- 分析结果缓存(30分钟) + +#### 3.3 历史记录管理 +**功能**:保存用户的预测历史 +**存储内容**: +- 问题描述 +- 起局时间 +- 预测结果 +- 准确性反馈 + +### 4. API接口设计 + +#### 4.1 起局接口 +```javascript +POST /api/qimen/calculate +{ + "datetime": "2024-03-15T10:00:00+08:00", + "timezone": "Asia/Shanghai", + "method": "时家" +} +``` + +#### 4.2 分析接口 +```javascript +POST /api/qimen/analyze +{ + "qimenPan": { /* 奇门盘数据 */ }, + "question": { + "type": "求财", + "description": "投资前景如何" + }, + "querent": { + "birthDate": "1990-05-15", + "gender": "男" + } +} +``` + +#### 4.3 预测接口 +```javascript +POST /api/qimen/predict +{ + "qimenPan": { /* 奇门盘数据 */ }, + "analysis": { /* 分析结果 */ }, + "options": { + "detailLevel": "full", + "includeAdvice": true + } +} +``` + +## 开发阶段规划 + +### 第一阶段:基础框架搭建(2周) + +**目标**:完成核心架构和基础功能 + +**任务清单**: +- [ ] 完善 QimenAnalyzer 基础类结构 +- [ ] 实现时间转换和干支计算 +- [ ] 完成节气计算模块 +- [ ] 建立基础数据结构 +- [ ] 实现简单的起局算法 +- [ ] 创建基础API接口 +- [ ] 编写单元测试 + +**交付物**: +- 可运行的基础奇门模块 +- 基础API接口 +- 单元测试覆盖率 > 80% + +### 第二阶段:核心算法实现(3周) + +**目标**:实现完整的奇门遁甲计算逻辑 + +**任务清单**: +- [ ] 完善三奇六仪排布算法 +- [ ] 实现九星八门八神排布 +- [ ] 完成值符值使计算 +- [ ] 实现格局识别算法 +- [ ] 完成用神选择逻辑 +- [ ] 实现旺衰判断算法 +- [ ] 优化计算性能 +- [ ] 完善错误处理 + +**交付物**: +- 完整的奇门起局功能 +- 准确的格局识别 +- 性能优化报告 + +### 第三阶段:分析预测功能(2周) + +**目标**:实现智能分析和预测生成 + +**任务清单**: +- [ ] 实现问题分类算法 +- [ ] 完成综合分析逻辑 +- [ ] 实现预测结果生成 +- [ ] 完成建议生成系统 +- [ ] 实现应期计算 +- [ ] 优化预测准确性 +- [ ] 完善用户体验 + +**交付物**: +- 智能分析系统 +- 预测结果生成器 +- 用户体验优化 + +### 第四阶段:前端界面开发(3周) + +**目标**:开发用户友好的前端界面 + +**任务清单**: +- [ ] 设计奇门盘显示组件 +- [ ] 实现起局页面 +- [ ] 开发分析结果页面 +- [ ] 创建历史记录功能 +- [ ] 实现数据导出功能 +- [ ] 优化移动端体验 +- [ ] 完善交互设计 +- [ ] 进行用户测试 + +**交付物**: +- 完整的前端界面 +- 响应式设计 +- 用户测试报告 + +### 第五阶段:系统集成测试(1周) + +**目标**:完成系统集成和全面测试 + +**任务清单**: +- [ ] 前后端集成测试 +- [ ] 性能压力测试 +- [ ] 准确性验证测试 +- [ ] 用户接受度测试 +- [ ] 安全性测试 +- [ ] 文档完善 +- [ ] 部署准备 + +**交付物**: +- 完整的奇门遁甲模块 +- 测试报告 +- 部署文档 + +## 技术实现细节 + +### 1. 核心算法实现 + +#### 起局算法核心逻辑 +```javascript +class QimenCalculator { + calculateQimenPan(datetime) { + // 1. 时间转换 + const timeInfo = this.convertToGanZhi(datetime); + + // 2. 节气判断 + const jieqi = this.calculateJieQi(datetime); + const yuan = this.calculateYuan(datetime, jieqi); + + // 3. 局数计算 + const { jushu, yindun } = this.calculateJuShu(jieqi, yuan); + + // 4. 地盘排布 + const dipan = this.arrangeDiPan(jushu, yindun); + + // 5. 天盘排布 + const tianpan = this.arrangeTianPan(dipan, timeInfo, yindun); + + // 6. 值符值使 + const { zhifu, zhishi } = this.calculateZhiFuZhiShi(dipan, tianpan, timeInfo); + + return { timeInfo, dipan, tianpan, zhifu, zhishi }; + } +} +``` + +#### 格局识别算法 +```javascript +class PatternAnalyzer { + analyzePatterns(qimenPan) { + const patterns = []; + + // 三奇格局识别 + patterns.push(...this.analyzeSanQiPatterns(qimenPan)); + + // 六仪格局识别 + patterns.push(...this.analyzeLiuYiPatterns(qimenPan)); + + // 特殊格局识别 + patterns.push(...this.analyzeSpecialPatterns(qimenPan)); + + // 格局评级 + return this.evaluatePatterns(patterns); + } +} +``` + +### 2. 性能优化策略 + +#### 缓存策略 +```javascript +class QimenCache { + constructor() { + this.panCache = new LRUCache({ max: 1000, ttl: 3600000 }); // 1小时 + this.jieqiCache = new LRUCache({ max: 100, ttl: 31536000000 }); // 1年 + this.analysisCache = new LRUCache({ max: 500, ttl: 1800000 }); // 30分钟 + } + + getCachedPan(timeKey) { + return this.panCache.get(timeKey); + } + + setCachedPan(timeKey, pan) { + this.panCache.set(timeKey, pan); + } +} +``` + +#### 批量计算优化 +```javascript +class BatchProcessor { + async calculateBatch(timeList) { + // 预计算公共数据 + const commonData = await this.preCalculateCommonData(); + + // 并行计算 + const promises = timeList.map(time => + this.calculateSingle(time, commonData) + ); + + return Promise.all(promises); + } +} +``` + +### 3. 数据结构设计 + +#### 奇门盘数据结构 +```javascript +const QimenPanSchema = { + timeInfo: { + year: { gan: String, zhi: String }, + month: { gan: String, zhi: String }, + day: { gan: String, zhi: String }, + hour: { gan: String, zhi: String }, + jieqi: String, + yuan: String, + jushu: Number, + yindun: Boolean + }, + dipan: [{ + ganzhi: String, + star: String, + door: String, + god: String, + palace: Number + }], + tianpan: [{ + ganzhi: String, + star: String, + door: String, + god: String, + palace: Number + }], + zhifu: String, + zhishi: String +}; +``` + +#### 分析结果数据结构 +```javascript +const AnalysisResultSchema = { + patterns: [{ + type: String, + name: String, + level: String, + description: String, + palace: Number, + score: Number + }], + yongshen: { + primary: String, + secondary: [String], + analysis: { + position: Number, + wangshui: String, + status: String + } + }, + prediction: { + overall: String, + probability: Number, + details: [String], + suggestions: [String], + timing: { + bestTime: String, + avoidTime: String + } + } +}; +``` + +## 测试策略 + +### 1. 单元测试 + +**测试范围**: +- 时间转换函数 +- 节气计算函数 +- 起局算法函数 +- 格局识别函数 +- 用神选择函数 + +**测试工具**:Jest +**覆盖率要求**:> 90% + +### 2. 集成测试 + +**测试场景**: +- 完整的起局流程 +- 分析预测流程 +- API接口测试 +- 缓存机制测试 + +**测试工具**:Supertest +**测试数据**:历史经典案例 + +### 3. 性能测试 + +**测试指标**: +- 起局计算时间 < 100ms +- 分析预测时间 < 500ms +- 并发处理能力 > 100 QPS +- 内存使用 < 512MB + +**测试工具**:Artillery + +### 4. 准确性测试 + +**测试方法**: +- 与传统手工起局对比 +- 历史案例验证 +- 专家评审 + +**准确率要求**:> 95% + +## 质量保证 + +### 1. 代码质量 +- ESLint 代码规范检查 +- Prettier 代码格式化 +- TypeScript 类型检查 +- SonarQube 代码质量分析 + +### 2. 文档质量 +- API文档自动生成 +- 代码注释覆盖率 > 80% +- 用户使用手册 +- 开发者文档 + +### 3. 安全性 +- 输入参数验证 +- SQL注入防护 +- XSS攻击防护 +- 访问频率限制 + +## 部署策略 + +### 1. 开发环境 +- Docker容器化部署 +- 热重载开发 +- 实时日志监控 + +### 2. 测试环境 +- 自动化部署 +- 持续集成测试 +- 性能监控 + +### 3. 生产环境 +- 蓝绿部署 +- 负载均衡 +- 监控告警 +- 自动扩缩容 + +## 风险评估与应对 + +### 1. 技术风险 + +**风险**:算法复杂度高,计算准确性难以保证 +**应对**: +- 分阶段验证算法正确性 +- 建立专家评审机制 +- 持续优化算法精度 + +**风险**:性能瓶颈,响应时间过长 +**应对**: +- 实施多级缓存策略 +- 优化算法复杂度 +- 采用异步处理机制 + +### 2. 业务风险 + +**风险**:用户接受度不高 +**应对**: +- 进行用户调研 +- 优化用户体验 +- 提供详细的使用指导 + +**风险**:预测准确性质疑 +**应对**: +- 建立反馈机制 +- 持续改进算法 +- 提供透明的计算过程 + +### 3. 项目风险 + +**风险**:开发周期延长 +**应对**: +- 合理分解任务 +- 建立里程碑检查 +- 预留缓冲时间 + +**风险**:人员技能不足 +**应对**: +- 提供技术培训 +- 引入外部专家 +- 建立知识分享机制 + +## 成功标准 + +### 1. 功能标准 +- [ ] 完整实现奇门遁甲起局功能 +- [ ] 准确识别各种格局组合 +- [ ] 智能选择用神和分析 +- [ ] 生成专业的预测报告 +- [ ] 支持多种问题类型 + +### 2. 性能标准 +- [ ] 起局计算时间 < 100ms +- [ ] 分析预测时间 < 500ms +- [ ] 系统可用性 > 99.5% +- [ ] 并发处理能力 > 100 QPS + +### 3. 质量标准 +- [ ] 代码测试覆盖率 > 90% +- [ ] 算法准确率 > 95% +- [ ] 用户满意度 > 4.5/5 +- [ ] 系统稳定性 > 99% + +### 4. 业务标准 +- [ ] 用户活跃度提升 > 30% +- [ ] 功能使用率 > 60% +- [ ] 用户反馈积极率 > 80% +- [ ] 专家认可度 > 85% + +## 后续优化方向 + +### 1. 功能扩展 +- 支持年家奇门、月家奇门 +- 增加风水应用功能 +- 开发移动端专属功能 +- 集成AI智能解读 + +### 2. 性能优化 +- 引入机器学习优化预测 +- 实现分布式计算 +- 优化数据存储结构 +- 提升算法执行效率 + +### 3. 用户体验 +- 个性化推荐系统 +- 社区交流功能 +- 学习教程系统 +- 专家咨询服务 + +### 4. 商业化 +- 高级功能付费订阅 +- 专业版本开发 +- API服务商业化 +- 合作伙伴生态 + +## 总结 + +本开发计划基于扎实的理论基础和技术架构,采用分阶段、迭代式的开发方式,确保项目的可控性和成功率。通过严格的质量控制和风险管理,力求打造一个专业、准确、易用的奇门遁甲分析系统,为传统文化的数字化传承做出贡献。 + +项目预计总开发周期为11周,涉及后端算法、前端界面、系统集成等多个方面。通过合理的资源配置和进度管理,确保按时交付高质量的产品。 \ No newline at end of file diff --git a/server/routes/qimen.cjs b/server/routes/qimen.cjs new file mode 100644 index 0000000..1c1ddb6 --- /dev/null +++ b/server/routes/qimen.cjs @@ -0,0 +1,551 @@ +// 奇门遁甲API路由 +// 提供奇门遁甲相关的RESTful接口 + +const express = require('express'); +const QimenAnalyzer = require('../services/qimenAnalyzer.cjs'); +const inputValidator = require('../utils/inputValidator.cjs'); +const logger = require('../middleware/logger.cjs'); + +const router = express.Router(); +const qimenAnalyzer = new QimenAnalyzer(); + +/** + * @route POST /api/qimen/calculate + * @desc 奇门遁甲起局计算 + * @access Public + */ +router.post('/calculate', async (req, res) => { + try { + const { timeInfo, options = {} } = req.body; + + // 输入验证 + if (!timeInfo || !timeInfo.datetime) { + return res.status(400).json({ + success: false, + error: { + code: 'INVALID_INPUT', + message: '缺少必要的时间信息', + details: 'datetime字段是必需的' + } + }); + } + + // 验证时间格式 + const datetime = new Date(timeInfo.datetime); + if (isNaN(datetime.getTime())) { + return res.status(400).json({ + success: false, + error: { + code: 'INVALID_TIME', + message: '时间格式不正确', + details: '请使用ISO 8601格式的时间字符串' + } + }); + } + + // 计算奇门盘 + const qimenPan = qimenAnalyzer.calculator.calculateQimenPan(datetime); + + // 构建响应数据 + const response = { + success: true, + data: { + timeInfo: { + datetime: timeInfo.datetime, + timezone: timeInfo.timezone || 'Asia/Shanghai', + ganzhi: { + year: `${qimenPan.timeInfo.year.gan}${qimenPan.timeInfo.year.zhi}`, + month: `${qimenPan.timeInfo.month.gan}${qimenPan.timeInfo.month.zhi}`, + day: `${qimenPan.timeInfo.day.gan}${qimenPan.timeInfo.day.zhi}`, + hour: `${qimenPan.timeInfo.hour.gan}${qimenPan.timeInfo.hour.zhi}` + }, + jieqi: qimenPan.timeInfo.jieqi, + yuan: qimenPan.timeInfo.yuan, + jushu: qimenPan.timeInfo.jushu, + yindun: qimenPan.timeInfo.yindun ? '阴遁' : '阳遁' + }, + qimenPan: { + dipan: qimenPan.dipan.map((item, index) => ({ + palace: index + 1, + palaceName: qimenAnalyzer.getPalaceName(index), + direction: qimenAnalyzer.getDirection(index), + ganzhi: item.ganzhi, + star: item.star, + door: item.door, + god: item.god + })), + tianpan: qimenPan.tianpan.map((item, index) => ({ + palace: index + 1, + palaceName: qimenAnalyzer.getPalaceName(index), + direction: qimenAnalyzer.getDirection(index), + ganzhi: item.ganzhi, + star: item.star, + door: item.door, + god: item.god + })), + zhifu: qimenPan.zhifu, + zhishi: qimenPan.zhishi + }, + method: options.method || '时家奇门' + } + }; + + logger.info('奇门起局成功', { + datetime: timeInfo.datetime, + jushu: qimenPan.timeInfo.jushu, + yindun: qimenPan.timeInfo.yindun + }); + + res.json(response); + + } catch (error) { + logger.error('奇门起局失败', error); + + res.status(500).json({ + success: false, + error: { + code: 'CALCULATION_ERROR', + message: '奇门盘计算失败', + details: error.message + } + }); + } +}); + +/** + * @route POST /api/qimen/analyze + * @desc 奇门遁甲格局分析 + * @access Public + */ +router.post('/analyze', async (req, res) => { + try { + const { qimenPan, analysisOptions = {} } = req.body; + + // 输入验证 + if (!qimenPan || !qimenPan.dipan || !qimenPan.tianpan) { + return res.status(400).json({ + success: false, + error: { + code: 'INVALID_INPUT', + message: '缺少奇门盘数据', + details: '需要提供完整的奇门盘信息' + } + }); + } + + // 格局分析 + const patterns = qimenAnalyzer.patternAnalyzer.analyzePatterns(qimenPan); + + // 构建响应数据 + const response = { + success: true, + data: { + patterns: patterns.map(pattern => ({ + name: pattern.name, + type: pattern.type, + level: pattern.level, + score: pattern.score, + palace: pattern.palace, + description: pattern.description + })), + summary: { + totalPatterns: patterns.length, + favorablePatterns: patterns.filter(p => ['吉', '大吉'].includes(p.level)).length, + unfavorablePatterns: patterns.filter(p => ['凶', '大凶'].includes(p.level)).length, + neutralPatterns: patterns.filter(p => ['中', '平'].includes(p.level)).length + }, + analysisOptions: { + includePatterns: analysisOptions.includePatterns !== false, + includeYongshen: analysisOptions.includeYongshen !== false, + detailLevel: analysisOptions.detailLevel || 'standard' + } + } + }; + + logger.info('奇门格局分析成功', { + patternsCount: patterns.length, + detailLevel: analysisOptions.detailLevel + }); + + res.json(response); + + } catch (error) { + logger.error('奇门格局分析失败', error); + + res.status(500).json({ + success: false, + error: { + code: 'ANALYSIS_ERROR', + message: '格局分析失败', + details: error.message + } + }); + } +}); + +/** + * @route POST /api/qimen/predict + * @desc 奇门遁甲预测生成 + * @access Public + */ +router.post('/predict', async (req, res) => { + try { + const { qimenPan, question, querent, options = {} } = req.body; + + // 输入验证 + if (!qimenPan || !question) { + return res.status(400).json({ + success: false, + error: { + code: 'INVALID_INPUT', + message: '缺少必要参数', + details: '需要提供奇门盘数据和问题描述' + } + }); + } + + // 验证问题描述 + if (!question.description || question.description.trim().length === 0) { + return res.status(400).json({ + success: false, + error: { + code: 'INVALID_QUESTION', + message: '问题描述不能为空', + details: '请提供具体的问题描述' + } + }); + } + + if (question.description.length > 200) { + return res.status(400).json({ + success: false, + error: { + code: 'QUESTION_TOO_LONG', + message: '问题描述过长', + details: '问题描述不能超过200个字符' + } + }); + } + + // 选择用神 + const yongshen = qimenAnalyzer.yongShenAnalyzer.selectYongShen( + question.description, + querent, + qimenPan + ); + + // 分析用神 + const yongShenAnalysis = qimenAnalyzer.yongShenAnalyzer.analyzeYongShen( + yongshen, + qimenPan + ); + + // 格局分析 + const patterns = qimenAnalyzer.patternAnalyzer.analyzePatterns(qimenPan); + + // 生成预测结果 + const prediction = qimenAnalyzer.predictionGenerator.generatePrediction( + qimenPan, + yongShenAnalysis, + question.description, + patterns + ); + + // 构建响应数据 + const response = { + success: true, + data: { + question: { + type: question.type || '其他', + description: question.description, + timeframe: question.timeframe || '近期' + }, + yongshen: { + primary: yongshen, + analysis: yongShenAnalysis + }, + prediction: { + overall: prediction.overall, + probability: prediction.probability, + details: prediction.details, + suggestions: prediction.suggestions, + timing: prediction.timing + }, + patterns: patterns.slice(0, 5), // 只返回前5个重要格局 + querent: querent ? { + birthDate: querent.birthDate, + gender: querent.gender + } : null + } + }; + + logger.info('奇门预测生成成功', { + questionType: question.type, + probability: prediction.probability, + patternsCount: patterns.length + }); + + res.json(response); + + } catch (error) { + logger.error('奇门预测生成失败', error); + + res.status(500).json({ + success: false, + error: { + code: 'PREDICTION_ERROR', + message: '预测生成失败', + details: error.message + } + }); + } +}); + +/** + * @route GET /api/qimen/solar-terms + * @desc 获取节气信息 + * @access Public + */ +router.get('/solar-terms', async (req, res) => { + try { + const { year = new Date().getFullYear() } = req.query; + + // 验证年份 + const yearNum = parseInt(year); + if (isNaN(yearNum) || yearNum < 1900 || yearNum > 2100) { + return res.status(400).json({ + success: false, + error: { + code: 'INVALID_YEAR', + message: '年份参数无效', + details: '年份必须在1900-2100之间' + } + }); + } + + // 计算节气 + const solarTerms = qimenAnalyzer.solarTerms.calculateYearSolarTerms(yearNum); + + const response = { + success: true, + data: solarTerms.map(term => ({ + name: term.name, + date: term.date.toISOString(), + yindun: qimenAnalyzer.solarTerms.isYindunSeason(term.name), + season: qimenAnalyzer.solarTerms.getSeasonByTerm(term.name), + info: qimenAnalyzer.solarTerms.getSolarTermInfo(term.name) + })) + }; + + res.json(response); + + } catch (error) { + logger.error('节气查询失败', error); + + res.status(500).json({ + success: false, + error: { + code: 'SOLAR_TERMS_ERROR', + message: '节气查询失败', + details: error.message + } + }); + } +}); + +/** + * @route GET /api/qimen/yongshen + * @desc 获取用神配置 + * @access Public + */ +router.get('/yongshen', async (req, res) => { + try { + const { type, gender } = req.query; + + if (!type) { + return res.status(400).json({ + success: false, + error: { + code: 'MISSING_TYPE', + message: '缺少问题类型参数', + details: '请提供type参数' + } + }); + } + + // 模拟用神配置(实际应该从分析器获取) + const yongShenConfig = { + '婚姻': { + primary: { + self: gender === '男' ? '庚' : '乙', + spouse: gender === '男' ? '乙' : '庚', + matchmaker: '六合' + }, + secondary: { + marriage_palace: '兑宫', + relationship_door: '休门' + } + }, + '求财': { + primary: { + wealth: '生门', + capital: '戊', + opportunity: '开门' + }, + secondary: { + wealth_palace: '艮宫', + profit_star: '天任' + } + }, + '疾病': { + primary: { + illness: '天芮', + doctor: '天心', + medicine: '乙' + }, + secondary: { + health_palace: '坤宫', + recovery_door: '生门' + } + } + }; + + const config = yongShenConfig[type] || { + primary: { + matter: '时干', + result: '值使' + }, + secondary: {} + }; + + res.json({ + success: true, + data: config + }); + + } catch (error) { + logger.error('用神查询失败', error); + + res.status(500).json({ + success: false, + error: { + code: 'YONGSHEN_ERROR', + message: '用神查询失败', + details: error.message + } + }); + } +}); + +/** + * @route POST /api/qimen/batch-calculate + * @desc 批量奇门起局计算 + * @access Public + */ +router.post('/batch-calculate', async (req, res) => { + try { + const { timeList, options = {} } = req.body; + + // 输入验证 + if (!Array.isArray(timeList) || timeList.length === 0) { + return res.status(400).json({ + success: false, + error: { + code: 'INVALID_INPUT', + message: '时间列表不能为空', + details: 'timeList必须是非空数组' + } + }); + } + + if (timeList.length > 10) { + return res.status(400).json({ + success: false, + error: { + code: 'TOO_MANY_REQUESTS', + message: '批量请求数量过多', + details: '单次批量请求不能超过10个时间点' + } + }); + } + + const results = []; + const errors = []; + + for (let i = 0; i < timeList.length; i++) { + try { + const datetime = new Date(timeList[i]); + if (isNaN(datetime.getTime())) { + errors.push({ + index: i, + datetime: timeList[i], + error: '时间格式不正确' + }); + continue; + } + + const qimenPan = qimenAnalyzer.calculator.calculateQimenPan(datetime); + + results.push({ + index: i, + datetime: timeList[i], + qimenPan: { + timeInfo: qimenPan.timeInfo, + jushu: qimenPan.timeInfo.jushu, + yindun: qimenPan.timeInfo.yindun ? '阴遁' : '阳遁', + zhifu: qimenPan.zhifu, + zhishi: qimenPan.zhishi + } + }); + + } catch (error) { + errors.push({ + index: i, + datetime: timeList[i], + error: error.message + }); + } + } + + res.json({ + success: true, + data: { + results, + errors, + summary: { + total: timeList.length, + successful: results.length, + failed: errors.length + } + } + }); + + } catch (error) { + logger.error('批量计算失败', error); + + res.status(500).json({ + success: false, + error: { + code: 'BATCH_CALCULATION_ERROR', + message: '批量计算失败', + details: error.message + } + }); + } +}); + +// 错误处理中间件 +router.use((error, req, res, next) => { + logger.error('奇门API错误', error); + + res.status(500).json({ + success: false, + error: { + code: 'INTERNAL_ERROR', + message: '服务器内部错误', + details: process.env.NODE_ENV === 'development' ? error.message : '请联系管理员' + } + }); +}); + +module.exports = router; \ No newline at end of file diff --git a/server/services/qimenAnalyzer.cjs b/server/services/qimenAnalyzer.cjs new file mode 100644 index 0000000..a612bad --- /dev/null +++ b/server/services/qimenAnalyzer.cjs @@ -0,0 +1,3940 @@ +// 奇门遁甲专业分析服务模块 +// 基于传统奇门遁甲理论的完整实现 + +const AnalysisCache = require('./common/AnalysisCache.cjs'); +const EnhancedRandom = require('./common/EnhancedRandom.cjs'); +const TimeConverter = require('../utils/timeConverter.cjs'); +const SolarTerms = require('../utils/solarTerms.cjs'); + +class QimenAnalyzer { + constructor() { + // 初始化基础数据 + this.initializeBasicData(); + this.initializePatterns(); + this.initializeSolarTerms(); + this.initializeWuXingData(); + this.initializeGanZhiData(); + + // 初始化缓存机制 + this.cache = new AnalysisCache({ + maxSize: 100, + defaultTTL: 3600000 // 1小时(奇门盘相对稳定) + }); + + // 初始化增强随机数生成器 + this.enhancedRandom = new EnhancedRandom(); + + // 初始化时间转换器 + this.timeConverter = new TimeConverter(); + + // 初始化节气计算器 + this.solarTerms = new SolarTerms(); + + // 初始化计算器和分析器 + this.calculator = new QimenCalculator(this); + this.patternAnalyzer = new PatternAnalyzer(this); + this.yongShenAnalyzer = new YongShenAnalyzer(this); + this.predictionGenerator = new PredictionGenerator(this); + } + + // 初始化基础数据 + initializeBasicData() { + // 天干地支 + this.TIANGAN = ['甲', '乙', '丙', '丁', '戊', '己', '庚', '辛', '壬', '癸']; + this.DIZHI = ['子', '丑', '寅', '卯', '辰', '巳', '午', '未', '申', '酉', '戌', '亥']; + + // 九星数据 + this.JIUXING = { + '天蓬': { element: '水', palace: 1, nature: '凶', meaning: '智谋、盗贼、暗昧之事' }, + '天任': { element: '土', palace: 2, nature: '吉', meaning: '田土、房屋、慈祥之事' }, + '天冲': { element: '木', palace: 3, nature: '凶', meaning: '冲动、急躁、军事之事' }, + '天辅': { element: '木', palace: 4, nature: '吉', meaning: '文书、教育、辅佐之事' }, + '天禽': { element: '土', palace: 5, nature: '吉', meaning: '中正、和合、调解之事' }, + '天心': { element: '金', palace: 6, nature: '吉', meaning: '医药、技艺、心灵之事' }, + '天柱': { element: '金', palace: 7, nature: '凶', meaning: '法律、支撑、阻碍之事' }, + '天芮': { element: '土', palace: 8, nature: '凶', meaning: '疾病、困顿、阻滞之事' }, + '天英': { element: '火', palace: 9, nature: '凶', meaning: '文明、礼仪、光明之事' } + }; + + // 八门数据 + this.BAMEN = { + '休门': { element: '水', palace: 1, nature: '吉', meaning: '休息、和谐、文书、学习、养生吉利' }, + '死门': { element: '土', palace: 2, nature: '凶', meaning: '死亡、坟墓、终结、吊丧、破财' }, + '伤门': { element: '木', palace: 3, nature: '凶', meaning: '伤害、竞争、体育、讨债、捕猎' }, + '杜门': { element: '木', palace: 4, nature: '中', meaning: '闭塞、隐藏、躲避、修炼、防守' }, + '中宫': { element: '土', palace: 5, nature: '中', meaning: '中央、调和、平衡' }, + '开门': { element: '金', palace: 6, nature: '吉', meaning: '开创、领导、官贵、求财、出行吉利' }, + '惊门': { element: '金', palace: 7, nature: '凶', meaning: '惊恐、官司、口舌、意外、变动' }, + '生门': { element: '土', palace: 8, nature: '吉', meaning: '生育、房地产、农业、求财、治病吉利' }, + '景门': { element: '火', palace: 9, nature: '中', meaning: '文采、考试、宴会、血光、官司' } + }; + + // 八神数据 + this.BASHEN = { + '值符': { nature: '吉', season: 'all', meaning: '主事者、领导、贵人、权威' }, + '螣蛇': { nature: '凶', season: 'all', meaning: '虚诈、惊恐、怪异、变化、文书纠纷' }, + '太阴': { nature: '吉', season: 'all', meaning: '阴私、暗昧、女性、隐秘、策划' }, + '六合': { nature: '吉', season: 'all', meaning: '和合、婚姻、合作、中介、媒人' }, + '白虎': { nature: '凶', season: 'autumn_winter', meaning: '凶猛、争斗、疾病、死亡、官司' }, + '勾陈': { nature: '凶', season: 'spring_summer', meaning: '田土、房屋、牢狱、纠缠、迟缓' }, + '朱雀': { nature: '凶', season: 'spring_summer', meaning: '文书、信息、口舌、官司、血光' }, + '玄武': { nature: '凶', season: 'autumn_winter', meaning: '盗贼、欺骗、暗昧、流动、智谋' }, + '九地': { nature: '吉', season: 'all', meaning: '坤土、柔顺、隐藏、农业、基础' }, + '九天': { nature: '吉', season: 'all', meaning: '乾金、刚健、高远、飞扬、创新' } + }; + + // 九宫方位 + this.JIUGONG = { + 1: { name: '坎宫', direction: '北', trigram: '坎', element: '水' }, + 2: { name: '坤宫', direction: '西南', trigram: '坤', element: '土' }, + 3: { name: '震宫', direction: '东', trigram: '震', element: '木' }, + 4: { name: '巽宫', direction: '东南', trigram: '巽', element: '木' }, + 5: { name: '中宫', direction: '中央', trigram: '中', element: '土' }, + 6: { name: '乾宫', direction: '西北', trigram: '乾', element: '金' }, + 7: { name: '兑宫', direction: '西', trigram: '兑', element: '金' }, + 8: { name: '艮宫', direction: '东北', trigram: '艮', element: '土' }, + 9: { name: '离宫', direction: '南', trigram: '离', element: '火' } + }; + } + + // 初始化五行数据 + initializeWuXingData() { + this.WUXING = { + '木': { + sheng: '火', // 木生火 + ke: '土', // 木克土 + shengBy: '水', // 水生木 + keBy: '金', // 金克木 + season: '春', + direction: ['东', '东南'], + color: '青' + }, + '火': { + sheng: '土', + ke: '金', + shengBy: '木', + keBy: '水', + season: '夏', + direction: ['南'], + color: '红' + }, + '土': { + sheng: '金', + ke: '水', + shengBy: '火', + keBy: '木', + season: '四季', + direction: ['中央', '西南', '东北'], + color: '黄' + }, + '金': { + sheng: '水', + ke: '木', + shengBy: '土', + keBy: '火', + season: '秋', + direction: ['西', '西北'], + color: '白' + }, + '水': { + sheng: '木', + ke: '火', + shengBy: '金', + keBy: '土', + season: '冬', + direction: ['北'], + color: '黑' + } + }; + } + + // 初始化干支数据 + initializeGanZhiData() { + this.GANZHI_WUXING = { + // 天干五行 + '甲': '木', '乙': '木', + '丙': '火', '丁': '火', + '戊': '土', '己': '土', + '庚': '金', '辛': '金', + '壬': '水', '癸': '水' + }; + + this.DIZHI_WUXING = { + // 地支五行 + '子': '水', '丑': '土', '寅': '木', '卯': '木', + '辰': '土', '巳': '火', '午': '火', '未': '土', + '申': '金', '酉': '金', '戌': '土', '亥': '水' + }; + + // 地支对应时辰 + this.DIZHI_TIME = { + '子': [23, 1], '丑': [1, 3], '寅': [3, 5], '卯': [5, 7], + '辰': [7, 9], '巳': [9, 11], '午': [11, 13], '未': [13, 15], + '申': [15, 17], '酉': [17, 19], '戌': [19, 21], '亥': [21, 23] + }; + + // 六甲旬首 + this.LIUJIA_XUN = { + '甲子': { empty: ['戌', '亥'], liuyi: '戊' }, + '甲戌': { empty: ['申', '酉'], liuyi: '己' }, + '甲申': { empty: ['午', '未'], liuyi: '庚' }, + '甲午': { empty: ['辰', '巳'], liuyi: '辛' }, + '甲辰': { empty: ['寅', '卯'], liuyi: '壬' }, + '甲寅': { empty: ['子', '丑'], liuyi: '癸' } + }; + } + + // 初始化格局模式 + initializePatterns() { + this.PATTERNS = { + // 三奇格局 + '乙奇得使': { type: '三奇', level: '吉', score: 15, description: '乙奇临值使门,主文书、合同、女性贵人相助' }, + '日奇伏吟': { type: '三奇', level: '凶', score: -10, description: '乙奇在本宫不动,主事情反复,女性有阻' }, + '月奇悖师': { type: '三奇', level: '凶', score: -15, description: '丙奇临太白星(庚),主官司、争斗' }, + '丙奇升殿': { type: '三奇', level: '大吉', score: 25, description: '丙奇临离宫,得地得时,主文明、礼仪大吉' }, + '玉女守门': { type: '三奇', level: '吉', score: 20, description: '丁奇临生门、开门、休门,主婚姻美满' }, + '星奇朝斗': { type: '三奇', level: '吉', score: 18, description: '丁奇临天心星,主医药、技艺精进' }, + + // 六仪格局 + '青龙返首': { type: '六仪', level: '吉', score: 12, description: '戊加己,主田土、房产大利' }, + '太白入荧': { type: '六仪', level: '凶', score: -12, description: '戊加庚,主争斗、官司' }, + '青龙折足': { type: '六仪', level: '凶', score: -10, description: '戊加辛,主破财、疾病' }, + '贵人入狱': { type: '六仪', level: '凶', score: -8, description: '己加戊,主贵人受困' }, + '地网高张': { type: '六仪', level: '凶', score: -15, description: '己加癸,主牢狱、困顿' }, + '太白逢星': { type: '六仪', level: '吉', score: 10, description: '庚加乙,主官司得理' }, + '亭亭之格': { type: '六仪', level: '吉', score: 15, description: '庚加丁,主婚姻、合作' }, + + // 特殊格局 + '伏吟': { type: '特殊', level: '不利', score: -5, description: '天盘与地盘同宫,主事情反复、迟缓、不动' }, + '反吟': { type: '特殊', level: '动荡', score: -8, description: '天盘与地盘相冲,主事情变化、动荡、不稳' }, + '飞宫': { type: '特殊', level: '变化', score: 0, description: '值符飞到其他宫位,主变化、升迁或降职' } + }; + } + + // 初始化节气数据 + initializeSolarTerms() { + this.SOLAR_TERMS = { + // 阳遁节气(冬至到夏至前) + '冬至': { yindun: false, formula: { '上元': 1, '中元': 7, '下元': 4 } }, + '小寒': { yindun: false, formula: { '上元': 2, '中元': 8, '下元': 5 } }, + '大寒': { yindun: false, formula: { '上元': 3, '中元': 9, '下元': 6 } }, + '立春': { yindun: false, formula: { '上元': 8, '中元': 5, '下元': 2 } }, + '雨水': { yindun: false, formula: { '上元': 9, '中元': 6, '下元': 3 } }, + '惊蛰': { yindun: false, formula: { '上元': 1, '中元': 7, '下元': 4 } }, + '春分': { yindun: false, formula: { '上元': 3, '中元': 9, '下元': 6 } }, + '清明': { yindun: false, formula: { '上元': 4, '中元': 1, '下元': 7 } }, + '谷雨': { yindun: false, formula: { '上元': 5, '中元': 2, '下元': 8 } }, + '立夏': { yindun: false, formula: { '上元': 4, '中元': 1, '下元': 7 } }, + '小满': { yindun: false, formula: { '上元': 5, '中元': 2, '下元': 8 } }, + '芒种': { yindun: false, formula: { '上元': 6, '中元': 3, '下元': 9 } }, + + // 阴遁节气(夏至到冬至前) + '夏至': { yindun: true, formula: { '上元': 9, '中元': 3, '下元': 6 } }, + '小暑': { yindun: true, formula: { '上元': 8, '中元': 2, '下元': 5 } }, + '大暑': { yindun: true, formula: { '上元': 7, '中元': 1, '下元': 4 } }, + '立秋': { yindun: true, formula: { '上元': 2, '中元': 5, '下元': 8 } }, + '处暑': { yindun: true, formula: { '上元': 1, '中元': 4, '下元': 7 } }, + '白露': { yindun: true, formula: { '上元': 9, '中元': 3, '下元': 6 } }, + '秋分': { yindun: true, formula: { '上元': 7, '中元': 1, '下元': 4 } }, + '寒露': { yindun: true, formula: { '上元': 6, '中元': 9, '下元': 3 } }, + '霜降': { yindun: true, formula: { '上元': 5, '中元': 8, '下元': 2 } }, + '立冬': { yindun: true, formula: { '上元': 6, '中元': 9, '下元': 3 } }, + '小雪': { yindun: true, formula: { '上元': 5, '中元': 8, '下元': 2 } }, + '大雪': { yindun: true, formula: { '上元': 4, '中元': 7, '下元': 1 } } + }; + } + + // 获取宫位名称 + getPalaceName(position) { + const palaceNames = [ + '坎一宫', '坤二宫', '震三宫', + '巽四宫', '中五宫', '乾六宫', + '兑七宫', '艮八宫', '离九宫' + ]; + return palaceNames[position] || '未知宫位'; + } + + // 获取五行关系 + getWuXingRelation(wuxing1, wuxing2) { + const relations = { + '木': { '火': '生', '土': '克', '金': '被克', '水': '被生', '木': '比和' }, + '火': { '土': '生', '金': '克', '水': '被克', '木': '被生', '火': '比和' }, + '土': { '金': '生', '水': '克', '木': '被克', '火': '被生', '土': '比和' }, + '金': { '水': '生', '木': '克', '火': '被克', '土': '被生', '金': '比和' }, + '水': { '木': '生', '火': '克', '土': '被克', '金': '被生', '水': '比和' } + }; + return relations[wuxing1]?.[wuxing2] || '无关'; + } + + // 获取干支五行 + getGanZhiWuXing(ganzhi) { + const ganWuxing = { + '甲': '木', '乙': '木', '丙': '火', '丁': '火', '戊': '土', + '己': '土', '庚': '金', '辛': '金', '壬': '水', '癸': '水' + }; + const zhiWuxing = { + '子': '水', '丑': '土', '寅': '木', '卯': '木', '辰': '土', + '巳': '火', '午': '火', '未': '土', '申': '金', '酉': '金', + '戌': '土', '亥': '水' + }; + return ganWuxing[ganzhi] || zhiWuxing[ganzhi] || null; + } + + // 判断是否为三奇 + isSanQi(element) { + return ['乙', '丙', '丁'].includes(element); + } + + // 获取当前季节 + getCurrentSeason(date) { + const month = date.getMonth(); + if (month >= 2 && month <= 4) return '春'; + if (month >= 5 && month <= 7) return '夏'; + if (month >= 8 && month <= 10) return '秋'; + return '冬'; + } + + /** + * 奇门遁甲分析主函数 + * @param {Object} inputData - 输入数据 + * @param {string} inputData.question - 占卜问题 + * @param {number} inputData.user_id - 用户ID + * @param {Object} inputData.birth_data - 出生数据(可选) + * @param {string} inputData.datetime - 起局时间(可选,默认当前时间) + * @returns {Object} 奇门遁甲分析结果 + */ + performQimenAnalysis(inputData) { + try { + // 输入参数验证 + if (!inputData || typeof inputData !== 'object') { + throw new Error('输入数据无效:必须提供有效的输入对象'); + } + + const { question, user_id, birth_data, datetime } = inputData; + + // 验证必要参数 + if (!question || typeof question !== 'string' || question.trim().length === 0) { + throw new Error('输入数据无效:问题不能为空'); + } + + if (question.length > 200) { + throw new Error('输入数据无效:问题长度不能超过200个字符'); + } + + if (user_id !== undefined && (!Number.isInteger(user_id) || user_id <= 0)) { + throw new Error('输入数据无效:用户ID必须是正整数'); + } + + // 检查缓存 + const cachedResult = this.cache.get('qimen', inputData); + if (cachedResult) { + return cachedResult; + } + + const currentTime = datetime ? new Date(datetime) : new Date(); + + // 起局 + const qimenPan = this.calculateQimenPan(currentTime); + + // 选择用神 + const yongShen = this.selectYongShen(question, birth_data, qimenPan); + + // 格局分析 + const patterns = this.analyzePatterns(qimenPan); + + // 用神分析 + const yongShenAnalysis = this.analyzeYongShen(yongShen, qimenPan); + + // 生成预测结果 + const prediction = this.generatePrediction(qimenPan, yongShenAnalysis, question, patterns); + + const result = { + analysis_type: 'qimen', + analysis_date: currentTime.toISOString(), + basic_info: { + divination_data: { + question: question.trim(), + divination_time: currentTime.toISOString(), + method: '时家奇门', + lunar_info: this.calculateLunarInfo(currentTime) + }, + qimen_info: { + jieqi: qimenPan.timeInfo.jieqi, + yuan: qimenPan.timeInfo.yuan, + jushu: qimenPan.timeInfo.jushu, + yindun: qimenPan.timeInfo.yindun ? '阴遁' : '阳遁', + zhifu: qimenPan.zhifu, + zhishi: qimenPan.zhishi, + ganzhi: { + year: qimenPan.timeInfo.year, + month: qimenPan.timeInfo.month, + day: qimenPan.timeInfo.day, + hour: qimenPan.timeInfo.hour + } + } + }, + detailed_analysis: { + qimen_pan: { + dipan: qimenPan.dipan, + tianpan: qimenPan.tianpan, + structure_analysis: this.analyzePanStructure(qimenPan) + }, + yongshen_analysis: yongShenAnalysis, + pattern_analysis: patterns, + wuxing_analysis: this.analyzeWuXing(qimenPan), + timing_analysis: this.analyzeTimingFactors(qimenPan, currentTime) + }, + prediction_result: prediction, + guidance: { + key_message: prediction.overall, + action_advice: prediction.suggestions, + timing_guidance: prediction.timing, + success_probability: prediction.probability + } + }; + + // 存储到缓存 + this.cache.set('qimen', inputData, result); + return result; + + } catch (error) { + console.error('奇门遁甲分析详细错误:', error); + throw error; + } + } + + // 计算奇门盘 + calculateQimenPan(datetime) { + const timeInfo = this.calculateTimeInfo(datetime); + const dipan = this.calculateDiPan(timeInfo); + const tianpan = this.calculateTianPan(dipan, timeInfo); + const { zhifu, zhishi } = this.calculateZhiFuZhiShi(dipan, tianpan, timeInfo); + + return { + timeInfo, + dipan, + tianpan, + zhifu, + zhishi + }; + } + + // 计算时间信息 + calculateTimeInfo(datetime) { + const year = datetime.getFullYear(); + const month = datetime.getMonth() + 1; + const day = datetime.getDate(); + const hour = datetime.getHours(); + + // 转换为干支 + const yearGZ = this.getYearGanZhi(year); + const monthGZ = this.getMonthGanZhi(year, month); + const dayGZ = this.getDayGanZhi(datetime); + const hourGZ = this.getHourGanZhi(hour, dayGZ.gan); + + // 计算节气 + const jieqi = this.calculateJieQi(datetime); + const yuan = this.calculateYuan(datetime, jieqi); + const { jushu, yindun } = this.calculateJuShu(jieqi, yuan); + + return { + year: yearGZ, + month: monthGZ, + day: dayGZ, + hour: hourGZ, + jieqi: jieqi.name, + yuan, + jushu, + yindun + }; + } + + // 计算地盘 + calculateDiPan(timeInfo) { + const dipan = new Array(9).fill(null).map(() => ({ + ganzhi: null, + star: null, + door: null, + god: null + })); + + // 排三奇六仪 + this.arrangeSanQiLiuYi(dipan, timeInfo.jushu, timeInfo.yindun); + + // 排九星 + this.arrangeJiuXing(dipan, timeInfo.jushu); + + // 排八门 + this.arrangeBaMen(dipan, timeInfo.hour.zhi); + + // 排八神 + this.arrangeBaShen(dipan, timeInfo.yindun); + + return dipan; + } + + // 计算天盘 + calculateTianPan(dipan, timeInfo) { + const tianpan = new Array(9).fill(null).map(() => ({ + ganzhi: null, + star: null, + door: null, + god: null + })); + + // 找到时干在地盘的位置 + const shiganPosition = this.findShiganPosition(dipan, timeInfo.hour.gan); + + // 按阴阳遁规律排列天盘 + if (timeInfo.yindun) { + this.arrangeYindunTianpan(tianpan, dipan, shiganPosition); + } else { + this.arrangeYangdunTianpan(tianpan, dipan, shiganPosition); + } + + return tianpan; + } + + // 计算值符值使 + calculateZhiFuZhiShi(dipan, tianpan, timeInfo) { + // 值符随时干 + const shiganPosition = this.findShiganPosition(dipan, timeInfo.hour.gan); + const zhifu = dipan[shiganPosition].star; + + // 值使随时支 + const zhizhiPosition = this.findZhizhiPosition(dipan, timeInfo.hour.zhi); + const zhishi = dipan[zhizhiPosition].door; + + return { zhifu, zhishi }; + } + + // 选择用神 + selectYongShen(question, birthData, qimenPan) { + const yongshen = {}; + + // 基础用神 + yongshen.rigan = qimenPan.timeInfo.day.gan; // 日干代表求测人 + + // 根据问题类型选择特殊用神 + const questionType = this.classifyQuestion(question); + + switch (questionType) { + case '婚姻': + yongshen.self = '乙'; // 女方 + yongshen.spouse = '庚'; // 男方 + yongshen.matchmaker = '六合'; + break; + + case '求财': + yongshen.wealth = '生门'; + yongshen.capital = '戊'; + yongshen.opportunity = '开门'; + break; + + case '疾病': + yongshen.illness = '天芮'; + yongshen.doctor = '天心'; + yongshen.medicine = '乙'; + break; + + case '官司': + yongshen.lawsuit = '景门'; + yongshen.judge = '值符'; + yongshen.opponent = this.getOpponentYongshen(yongshen.rigan); + break; + + case '求职': + yongshen.job = '开门'; + yongshen.company = '值符'; + yongshen.process = '值使'; + break; + + default: + yongshen.matter = '时干'; + yongshen.result = '值使'; + } + + return yongshen; + } + + // 分析格局 + analyzePatterns(qimenPan) { + const patterns = []; + + // 分析三奇格局 + patterns.push(...this.analyzeSanQiPatterns(qimenPan)); + + // 分析六仪格局 + patterns.push(...this.analyzeLiuYiPatterns(qimenPan)); + + // 分析特殊格局 + patterns.push(...this.analyzeSpecialPatterns(qimenPan)); + + return patterns; + } + + // 分析用神 + analyzeYongShen(yongshen, qimenPan) { + const analysis = {}; + + for (const [key, value] of Object.entries(yongshen)) { + const position = this.findYongShenPosition(value, qimenPan); + if (position !== -1) { + analysis[key] = { + position, + palace: this.JIUGONG[position + 1].name, + wangshui: this.calculateWangShui(value, qimenPan.timeInfo), + door: qimenPan.tianpan[position].door, + star: qimenPan.tianpan[position].star, + god: qimenPan.tianpan[position].god, + status: this.evaluateYongShenStatus(position, qimenPan) + }; + } + } + + return analysis; + } + + // 生成预测结果 + /** + * 生成预测结果 + * @param {Object} qimenPan - 奇门盘 + * @param {Object} yongShenAnalysis - 用神分析 + * @param {string} question - 问题描述 + * @param {Array} patterns - 格局列表 + * @returns {Object} 预测结果 + */ + generatePrediction(qimenPan, yongShenAnalysis, question, patterns) { + // 计算应期 + const timing = this.calculateTiming(qimenPan, yongShenAnalysis, patterns); + + // 计算成功概率 + const probability = this.calculateProbability(yongShenAnalysis, patterns); + + // 生成整体评价 + const overall = this.generateOverallAssessment(probability, yongShenAnalysis); + + // 生成详细分析 + const details = this.generateDetailedAnalysis(qimenPan, yongShenAnalysis, patterns); + + // 生成建议 + const suggestions = this.generateSuggestions(yongShenAnalysis, patterns, timing); + + return { + overall, + probability, + details, + suggestions, + timing: timing.description, + timingDetails: timing, + favorableTime: timing.favorableTime, + unfavorableTime: timing.unfavorableTime + }; + } + + /** + * 计算应期 + * @param {Object} qimenPan - 奇门盘 + * @param {Object} yongShenAnalysis - 用神分析 + * @param {Array} patterns - 格局列表 + * @returns {Object} 应期分析 + */ + calculateTiming(qimenPan, yongShenAnalysis, patterns) { + const timingFactors = []; + + // 基于用神旺衰的应期 + const yongShenTiming = this.calculateYongShenTiming(yongShenAnalysis); + timingFactors.push(yongShenTiming); + + // 基于格局的应期 + const patternTiming = this.calculatePatternTiming(patterns); + timingFactors.push(patternTiming); + + // 基于节气的应期 + const seasonalTiming = this.calculateSeasonalTiming(qimenPan); + timingFactors.push(seasonalTiming); + + // 基于值符值使的应期 + const zhifuzhishiTiming = this.calculateZhifuzhishiTiming(qimenPan); + timingFactors.push(zhifuzhishiTiming); + + // 综合应期判断 + return this.synthesizeTiming(timingFactors); + } + + /** + * 基于用神计算应期 + * @param {Object} yongShenAnalysis - 用神分析 + * @returns {Object} 用神应期 + */ + calculateYongShenTiming(yongShenAnalysis) { + const { overall } = yongShenAnalysis; + + if (overall.favorability >= 70) { + return { + type: '用神应期', + timing: '近期', + score: 3, + description: '用神状态佳,近期有利', + timeframe: '1-3个月' + }; + } else if (overall.favorability >= 50) { + return { + type: '用神应期', + timing: '中期', + score: 1, + description: '用神状态中等,需等待时机', + timeframe: '3-6个月' + }; + } else { + return { + type: '用神应期', + timing: '远期', + score: -1, + description: '用神状态不佳,需长期等待', + timeframe: '6个月以上' + }; + } + } + + /** + * 基于格局计算应期 + * @param {Array} patterns - 格局列表 + * @returns {Object} 格局应期 + */ + calculatePatternTiming(patterns) { + if (!patterns || patterns.length === 0) { + return { + type: '格局应期', + timing: '中期', + score: 0, + description: '无明显格局,时机平常', + timeframe: '3-6个月' + }; + } + + // 找到最重要的格局 + const topPattern = patterns[0]; + + if (topPattern.score >= 20) { + return { + type: '格局应期', + timing: '近期', + score: 3, + description: `${topPattern.name}格局大吉,近期应验`, + timeframe: '1-2个月' + }; + } else if (topPattern.score >= 10) { + return { + type: '格局应期', + timing: '中期', + score: 2, + description: `${topPattern.name}格局较吉,中期应验`, + timeframe: '2-4个月' + }; + } else if (topPattern.score <= -10) { + return { + type: '格局应期', + timing: '不利', + score: -2, + description: `${topPattern.name}格局不利,需避开`, + timeframe: '暂不宜行动' + }; + } else { + return { + type: '格局应期', + timing: '中期', + score: 0, + description: '格局平常,时机一般', + timeframe: '3-6个月' + }; + } + } + + /** + * 基于节气计算应期 + * @param {Object} qimenPan - 奇门盘 + * @returns {Object} 节气应期 + */ + calculateSeasonalTiming(qimenPan) { + const currentSeason = this.analyzer.getCurrentSeason(new Date()); + const nextSeason = this.getNextSeason(currentSeason); + + // 根据当前节气判断应期 + const seasonalStrength = this.getSeasonalStrength(qimenPan, currentSeason); + + if (seasonalStrength >= 2) { + return { + type: '节气应期', + timing: '当季', + score: 2, + description: `${currentSeason}季有利,当季应验`, + timeframe: '本季度内' + }; + } else if (seasonalStrength <= -2) { + return { + type: '节气应期', + timing: '来季', + score: 1, + description: `${currentSeason}季不利,${nextSeason}季转机`, + timeframe: '下个季度' + }; + } else { + return { + type: '节气应期', + timing: '平常', + score: 0, + description: '节气影响平常', + timeframe: '不受季节限制' + }; + } + } + + /** + * 基于值符值使计算应期 + * @param {Object} qimenPan - 奇门盘 + * @returns {Object} 值符值使应期 + */ + calculateZhifuzhishiTiming(qimenPan) { + const zhifuzhishiInfo = qimenPan.details; + + if (!zhifuzhishiInfo || !zhifuzhishiInfo.relationship) { + return { + type: '值符值使应期', + timing: '中期', + score: 0, + description: '值符值使关系平常', + timeframe: '3-6个月' + }; + } + + const { relationship } = zhifuzhishiInfo; + + if (relationship.isHarmony) { + return { + type: '值符值使应期', + timing: '近期', + score: 2, + description: '值符值使和谐,近期有利', + timeframe: '1-3个月' + }; + } else { + return { + type: '值符值使应期', + timing: '中期', + score: -1, + description: '值符值使不和,需等待时机', + timeframe: '3-6个月' + }; + } + } + + /** + * 综合应期判断 + * @param {Array} timingFactors - 应期因素 + * @returns {Object} 综合应期 + */ + synthesizeTiming(timingFactors) { + const totalScore = timingFactors.reduce((sum, factor) => sum + factor.score, 0); + const averageScore = totalScore / timingFactors.length; + + let mainTiming, description, timeframe; + + if (averageScore >= 2) { + mainTiming = '近期'; + description = '多项因素有利,近期应验'; + timeframe = '1-3个月'; + } else if (averageScore >= 1) { + mainTiming = '中近期'; + description = '整体趋势向好,中近期应验'; + timeframe = '2-4个月'; + } else if (averageScore >= 0) { + mainTiming = '中期'; + description = '时机平常,中期应验'; + timeframe = '3-6个月'; + } else if (averageScore >= -1) { + mainTiming = '中远期'; + description = '需要等待,中远期应验'; + timeframe = '6-12个月'; + } else { + mainTiming = '远期'; + description = '时机不利,需长期等待'; + timeframe = '12个月以上'; + } + + // 确定有利和不利时间 + const favorableTime = this.getFavorableTime(timingFactors); + const unfavorableTime = this.getUnfavorableTime(timingFactors); + + return { + description, + mainTiming, + timeframe, + score: averageScore, + factors: timingFactors, + favorableTime, + unfavorableTime + }; + } + + /** + * 获取有利时间 + * @param {Array} timingFactors - 应期因素 + * @returns {Array} 有利时间列表 + */ + getFavorableTime(timingFactors) { + const favorableFactors = timingFactors.filter(factor => factor.score > 0); + + return favorableFactors.map(factor => ({ + period: factor.timeframe, + reason: factor.description, + strength: factor.score + })); + } + + /** + * 获取不利时间 + * @param {Array} timingFactors - 应期因素 + * @returns {Array} 不利时间列表 + */ + getUnfavorableTime(timingFactors) { + const unfavorableFactors = timingFactors.filter(factor => factor.score < 0); + + return unfavorableFactors.map(factor => ({ + period: factor.timeframe, + reason: factor.description, + strength: Math.abs(factor.score) + })); + } + + /** + * 获取下个季节 + * @param {string} currentSeason - 当前季节 + * @returns {string} 下个季节 + */ + getNextSeason(currentSeason) { + const seasonCycle = ['春', '夏', '秋', '冬']; + const currentIndex = seasonCycle.indexOf(currentSeason); + return seasonCycle[(currentIndex + 1) % 4]; + } + + /** + * 获取节气强度 + * @param {Object} qimenPan - 奇门盘 + * @param {string} season - 季节 + * @returns {number} 节气强度 + */ + getSeasonalStrength(qimenPan, season) { + // 简化的节气强度计算 + const seasonWuxingMap = { + '春': '木', + '夏': '火', + '秋': '金', + '冬': '水' + }; + + const seasonWuxing = seasonWuxingMap[season]; + let strength = 0; + + // 检查主要用神与季节的关系 + if (qimenPan.timeInfo && qimenPan.timeInfo.day) { + const riganWuxing = this.analyzer.getGanZhiWuXing(qimenPan.timeInfo.day.gan); + if (riganWuxing) { + const relation = this.analyzer.getWuXingRelation(seasonWuxing, riganWuxing); + if (relation === '生') strength += 2; + else if (relation === '比和') strength += 1; + else if (relation === '克') strength -= 2; + } + } + + return strength; + } + + /** + * 计算成功概率 + * @param {Object} yongShenAnalysis - 用神分析 + * @param {Array} patterns - 格局列表 + * @returns {number} 成功概率(0-100) + */ + calculateProbability(yongShenAnalysis, patterns) { + let baseProbability = yongShenAnalysis.overall.favorability || 50; + + // 根据格局调整概率 + if (patterns && patterns.length > 0) { + const patternScore = patterns.reduce((sum, pattern) => sum + pattern.score, 0); + const patternAdjustment = Math.min(Math.max(patternScore, -20), 20); + baseProbability += patternAdjustment; + } + + // 确保概率在合理范围内 + return Math.min(Math.max(baseProbability, 10), 90); + } + + /** + * 生成整体评价 + * @param {number} probability - 成功概率 + * @param {Object} yongShenAnalysis - 用神分析 + * @returns {string} 整体评价 + */ + generateOverallAssessment(probability, yongShenAnalysis) { + if (probability >= 80) { + return '非常有利,成功可能性很大'; + } else if (probability >= 70) { + return '比较有利,成功可能性较大'; + } else if (probability >= 60) { + return '相对有利,有一定成功可能'; + } else if (probability >= 50) { + return '中等,需要努力争取'; + } else if (probability >= 40) { + return '相对不利,需要谨慎行动'; + } else if (probability >= 30) { + return '比较不利,建议暂缓行动'; + } else { + return '很不利,不建议行动'; + } + } + + // 计算综合评分 + calculateOverallScore(yongShenAnalysis, patterns) { + let score = 50; // 基础分 + + // 用神旺衰评分 + for (const [key, analysis] of Object.entries(yongShenAnalysis)) { + switch (analysis.wangshui) { + case '旺': score += 15; break; + case '相': score += 10; break; + case '休': score += 0; break; + case '囚': score -= 10; break; + case '死': score -= 15; break; + } + } + + // 格局评分 + for (const pattern of patterns) { + switch (pattern.level) { + case '大吉': score += 20; break; + case '吉': score += 10; break; + case '中': score += 0; break; + case '凶': score -= 10; break; + case '大凶': score -= 20; break; + } + } + + return Math.max(0, Math.min(100, score)); + } + + // 解释评分 + interpretScore(score) { + if (score >= 80) return '大吉,事情非常顺利'; + if (score >= 65) return '较为有利,成功概率较高'; + if (score >= 50) return '中等,需要努力争取'; + if (score >= 35) return '较为困难,需要谨慎行事'; + return '不利,建议暂缓或改变策略'; + } + + // 辅助方法:计算农历信息 + calculateLunarInfo(datetime) { + // 简化实现,实际应用中需要更精确的农历转换 + return { + year: '甲辰', + month: '丁卯', + day: '庚申', + description: '农历信息(简化版)' + }; + } + + // 辅助方法:分类问题类型 + classifyQuestion(question) { + const keywords = { + '婚姻': ['婚姻', '结婚', '恋爱', '感情', '配偶', '对象'], + '求财': ['财运', '投资', '生意', '赚钱', '收入', '财富'], + '疾病': ['健康', '疾病', '治病', '医院', '身体', '病情'], + '官司': ['官司', '诉讼', '法律', '纠纷', '争议', '法院'], + '求职': ['工作', '求职', '面试', '职业', '就业', '跳槽'] + }; + + for (const [type, words] of Object.entries(keywords)) { + if (words.some(word => question.includes(word))) { + return type; + } + } + + return '其他'; + } + + // 核心工具方法 + + /** + * 获取五行生克关系 + * @param {string} element1 - 第一个五行 + * @param {string} element2 - 第二个五行 + * @returns {string} 生克关系 + */ + getWuXingRelation(element1, element2) { + if (!this.WUXING[element1] || !this.WUXING[element2]) { + return '无关系'; + } + + if (this.WUXING[element1].sheng === element2) { + return '生'; + } else if (this.WUXING[element1].ke === element2) { + return '克'; + } else if (this.WUXING[element1].shengBy === element2) { + return '被生'; + } else if (this.WUXING[element1].keBy === element2) { + return '被克'; + } else if (element1 === element2) { + return '比和'; + } + + return '无关系'; + } + + /** + * 获取干支五行属性 + * @param {string} ganZhi - 天干或地支 + * @returns {string} 五行属性 + */ + getGanZhiWuXing(ganZhi) { + return this.GANZHI_WUXING[ganZhi] || this.DIZHI_WUXING[ganZhi] || '未知'; + } + + /** + * 判断是否为三奇 + * @param {string} gan - 天干 + * @returns {boolean} 是否为三奇 + */ + isSanQi(gan) { + return ['乙', '丙', '丁'].includes(gan); + } + + /** + * 判断是否为六仪 + * @param {string} gan - 天干 + * @returns {boolean} 是否为六仪 + */ + isLiuYi(gan) { + return ['戊', '己', '庚', '辛', '壬', '癸'].includes(gan); + } + + /** + * 获取宫位名称 + * @param {number} palace - 宫位数字(0-8) + * @returns {string} 宫位名称 + */ + getPalaceName(palace) { + return this.JIUGONG[palace + 1]?.name || '未知宫'; + } + + /** + * 获取方位信息 + * @param {number} palace - 宫位数字(0-8) + * @returns {string} 方位 + */ + getDirection(palace) { + return this.JIUGONG[palace + 1]?.direction || '未知方位'; + } + + /** + * 计算旺衰状态 + * @param {string} element - 五行 + * @param {string} season - 季节 + * @returns {string} 旺衰状态 + */ + calculateWangShuiByElement(element, season) { + const elementData = this.WUXING[element]; + if (!elementData) return '未知'; + + if (elementData.season === season) { + return '旺'; + } + + // 相:我生者为相 + const shengElement = this.WUXING[elementData.sheng]; + if (shengElement && shengElement.season === season) { + return '相'; + } + + // 休:生我者为休 + const shengByElement = this.WUXING[elementData.shengBy]; + if (shengByElement && shengByElement.season === season) { + return '休'; + } + + // 囚:克我者为囚 + const keByElement = this.WUXING[elementData.keBy]; + if (keByElement && keByElement.season === season) { + return '囚'; + } + + // 死:我克者为死 + const keElement = this.WUXING[elementData.ke]; + if (keElement && keElement.season === season) { + return '死'; + } + + return '平'; + } + + /** + * 获取当前季节 + * @param {Date} datetime - 日期时间 + * @returns {string} 季节 + */ + getCurrentSeason(datetime) { + const month = datetime.getMonth() + 1; + + if (month >= 3 && month <= 5) return '春'; + if (month >= 6 && month <= 8) return '夏'; + if (month >= 9 && month <= 11) return '秋'; + return '冬'; + } + + // 时间转换方法(使用TimeConverter) + getYearGanZhi(year) { + return this.timeConverter.getYearGanZhi(year); + } + + getMonthGanZhi(year, month) { + return this.timeConverter.getMonthGanZhi(year, month); + } + + getDayGanZhi(datetime) { + return this.timeConverter.getDayGanZhi(datetime); + } + + getHourGanZhi(hour, dayGan) { + return this.timeConverter.getHourGanZhi(hour, dayGan); + } + + // 节气计算方法(使用SolarTerms) + calculateJieQi(datetime) { + return this.solarTerms.getCurrentSolarTerm(datetime); + } + + calculateYuan(datetime, jieqi) { + return this.solarTerms.calculateYuan(datetime, jieqi); + } + + calculateJuShu(jieqi, yuan) { + const solarTermData = this.SOLAR_TERMS[jieqi.name]; + if (!solarTermData) { + throw new Error(`未知节气: ${jieqi.name}`); + } + + const jushu = solarTermData.formula[yuan]; + const yindun = solarTermData.yindun; + + return { jushu, yindun }; + } + + /** + * 排布三奇六仪到地盘 + * @param {Array} dipan - 地盘数组 + * @param {number} jushu - 局数 + * @param {boolean} yindun - 是否阴遁 + * @returns {Object} 排布信息和格局提示 + */ + arrangeSanQiLiuYi(dipan, jushu, yindun) { + // 三奇六仪完整序列 + const ganzhiSequence = ['戊', '己', '庚', '辛', '壬', '癸', '丁', '丙', '乙']; + const ganzhiTypes = ['六仪', '六仪', '六仪', '六仪', '六仪', '六仪', '三奇', '三奇', '三奇']; + + // 根据局数确定戊的起始位置 + const startPosition = this.getWuPositionByJushu(jushu); + + // 记录排布信息 + const arrangementInfo = { + startPosition, + yindun, + jushu, + ganzhiPositions: {}, + potentialPatterns: [] + }; + + // 按照传统奇门遁甲规律排布 + for (let i = 0; i < 9; i++) { + let position; + + if (yindun) { + // 阴遁:逆时针排列 + position = (startPosition - i + 9) % 9; + } else { + // 阳遁:顺时针排列 + position = (startPosition + i) % 9; + } + + if (dipan[position]) { + const ganzhi = ganzhiSequence[i]; + dipan[position].ganzhi = ganzhi; + dipan[position].ganzhiType = ganzhiTypes[i]; + dipan[position].ganzhiIndex = i; + + // 记录干支位置信息 + arrangementInfo.ganzhiPositions[ganzhi] = { + palace: position, + palaceName: this.getPalaceName(position), + type: ganzhiTypes[i], + wuxing: this.getGanZhiWuXing(ganzhi) + }; + + // 检查潜在格局 + this.checkPotentialPatterns(ganzhi, position, arrangementInfo); + } + } + + return arrangementInfo; + } + + /** + * 检查潜在的格局组合 + * @param {string} ganzhi - 当前干支 + * @param {number} position - 当前位置 + * @param {Object} arrangementInfo - 排布信息 + */ + checkPotentialPatterns(ganzhi, position, arrangementInfo) { + const palaceName = this.getPalaceName(position); + + // 检查三奇特殊位置 + if (['乙', '丙', '丁'].includes(ganzhi)) { + // 三奇到特定宫位的格局提示 + const specialPositions = { + '乙': { '离宫': '日奇升殿', '坎宫': '日奇入墓' }, + '丙': { '离宫': '月奇升殿', '坎宫': '月奇入墓' }, + '丁': { '离宫': '星奇升殿', '坎宫': '星奇入墓' } + }; + + if (specialPositions[ganzhi] && specialPositions[ganzhi][palaceName]) { + arrangementInfo.potentialPatterns.push({ + name: specialPositions[ganzhi][palaceName], + type: '三奇格局', + ganzhi, + palace: position, + palaceName + }); + } + } + + // 检查六仪特殊组合 + if (['戊', '己', '庚', '辛', '壬', '癸'].includes(ganzhi)) { + // 六仪的特殊格局检查将在后续完善 + if (ganzhi === '戊' && palaceName === '中宫') { + arrangementInfo.potentialPatterns.push({ + name: '戊居中宫', + type: '六仪格局', + ganzhi, + palace: position, + palaceName + }); + } + } + } + + /** + * 根据局数获取戊的位置 + * @param {number} jushu - 局数 + * @returns {number} 戊的位置(0-8) + */ + getWuPositionByJushu(jushu) { + // 局数对应戊的位置(按照奇门遁甲传统规律) + const wuPositions = { + 1: 0, // 一局戊在坎宫 + 2: 1, // 二局戊在坤宫 + 3: 2, // 三局戊在震宫 + 4: 3, // 四局戊在巽宫 + 5: 4, // 五局戊在中宫 + 6: 5, // 六局戊在乾宫 + 7: 6, // 七局戊在兑宫 + 8: 7, // 八局戊在艮宫 + 9: 8 // 九局戊在离宫 + }; + + return wuPositions[jushu] !== undefined ? wuPositions[jushu] : 0; + } + + /** + * 排布九星到地盘 + * @param {Array} dipan - 地盘数组 + * @param {number} jushu - 局数 + * @param {boolean} yindun - 是否阴遁 + * @returns {Object} 九星排布信息 + */ + arrangeJiuXing(dipan, jushu, yindun) { + // 九星序列(按洛书顺序) + const stars = ['天蓬', '天任', '天冲', '天辅', '天英', '天芮', '天柱', '天心', '天禽']; + const starWuxing = ['水', '土', '木', '木', '火', '土', '金', '金', '土']; + const starNumbers = [1, 2, 3, 4, 5, 6, 7, 8, 9]; + + // 根据局数和阴阳遁确定天英星的位置 + const tianYingPosition = this.getTianYingPositionByJushu(jushu, yindun); + + const starInfo = { + tianYingPosition, + starPositions: {}, + starRelations: [] + }; + + // 排布九星 + for (let i = 0; i < 9; i++) { + let position; + + if (yindun) { + // 阴遁:从天英星位置逆时针排列 + position = (tianYingPosition - (i - 4) + 9) % 9; + } else { + // 阳遁:从天英星位置顺时针排列 + position = (tianYingPosition + (i - 4) + 9) % 9; + } + + if (dipan[position]) { + const star = stars[i]; + dipan[position].star = star; + dipan[position].starWuxing = starWuxing[i]; + dipan[position].starNumber = starNumbers[i]; + + // 记录星位信息 + starInfo.starPositions[star] = { + palace: position, + palaceName: this.getPalaceName(position), + wuxing: starWuxing[i], + number: starNumbers[i], + isZhifu: false // 将在值符计算时更新 + }; + + // 分析星宫关系 + this.analyzeStarPalaceRelation(star, position, starInfo); + } + } + + return starInfo; + } + + /** + * 分析星宫关系 + * @param {string} star - 星名 + * @param {number} position - 宫位 + * @param {Object} starInfo - 星位信息 + */ + analyzeStarPalaceRelation(star, position, starInfo) { + const palaceName = this.getPalaceName(position); + const starWuxing = this.JIUXING[star]?.element; + const palaceWuxing = this.JIUGONG[position + 1]?.element; + + if (starWuxing && palaceWuxing) { + const relation = this.getWuXingRelation(starWuxing, palaceWuxing); + + starInfo.starRelations.push({ + star, + palace: position, + palaceName, + relation, + starWuxing, + palaceWuxing, + isHarmony: ['生', '比和'].includes(relation) + }); + } + } + + /** + * 根据局数获取天英星位置 + * @param {number} jushu - 局数 + * @param {boolean} yindun - 是否阴遁 + * @returns {number} 天英星位置(0-8) + */ + getTianYingPositionByJushu(jushu, yindun) { + // 天英星固定在离九宫(位置8),其他星按局数和阴阳遁排布 + // 这里返回的是整个九星排布的基准位置 + const basePositions = { + 1: 0, // 一局:坎宫为基准 + 2: 1, // 二局:坤宫为基准 + 3: 2, // 三局:震宫为基准 + 4: 3, // 四局:巽宫为基准 + 5: 4, // 五局:中宫为基准 + 6: 5, // 六局:乾宫为基准 + 7: 6, // 七局:兑宫为基准 + 8: 7, // 八局:艮宫为基准 + 9: 8 // 九局:离宫为基准 + }; + + return basePositions[jushu] !== undefined ? basePositions[jushu] : 8; + } + + /** + * 排布八门到地盘 + * @param {Array} dipan - 地盘数组 + * @param {string} hourZhi - 时支 + * @param {boolean} yindun - 是否阴遁 + * @returns {Object} 八门排布信息 + */ + arrangeBaMen(dipan, hourZhi, yindun) { + // 八门序列(按洛书顺序,跳过中宫) + const doors = ['休门', '死门', '伤门', '杜门', '景门', '惊门', '开门', '生门']; + const doorWuxing = ['水', '土', '木', '木', '火', '金', '金', '土']; + const doorAttributes = ['吉', '凶', '凶', '凶', '凶', '凶', '大吉', '大吉']; + + // 根据时支确定值使门的位置 + const zhishiPosition = this.getZhishiPositionByZhi(hourZhi); + + const doorInfo = { + zhishiPosition, + zhishiDoor: null, + doorPositions: {}, + doorRelations: [] + }; + + // 八宫位置映射(跳过中宫) + const palaceMapping = [0, 1, 2, 3, 5, 6, 7, 8]; // 跳过位置4(中宫) + + // 排布八门 + for (let i = 0; i < 8; i++) { + let doorIndex; + + if (yindun) { + // 阴遁:逆时针排列 + doorIndex = (zhishiPosition - i + 8) % 8; + } else { + // 阳遁:顺时针排列 + doorIndex = (zhishiPosition + i) % 8; + } + + const position = palaceMapping[doorIndex]; + const door = doors[i]; + + if (dipan[position]) { + dipan[position].door = door; + dipan[position].doorWuxing = doorWuxing[i]; + dipan[position].doorAttribute = doorAttributes[i]; + + // 记录门位信息 + doorInfo.doorPositions[door] = { + palace: position, + palaceName: this.getPalaceName(position), + wuxing: doorWuxing[i], + attribute: doorAttributes[i], + isZhishi: i === 0 // 第一个门是值使门 + }; + + if (i === 0) { + doorInfo.zhishiDoor = door; + } + + // 分析门宫关系 + this.analyzeDoorPalaceRelation(door, position, doorInfo); + } + } + + // 中宫不排门 + if (dipan[4]) { + dipan[4].door = null; + dipan[4].doorWuxing = null; + dipan[4].doorAttribute = null; + } + + return doorInfo; + } + + /** + * 分析门宫关系 + * @param {string} door - 门名 + * @param {number} position - 宫位 + * @param {Object} doorInfo - 门位信息 + */ + analyzeDoorPalaceRelation(door, position, doorInfo) { + const palaceName = this.getPalaceName(position); + const doorWuxing = this.BAMEN[door]?.element; + const palaceWuxing = this.JIUGONG[position + 1]?.element; + + if (doorWuxing && palaceWuxing) { + const relation = this.getWuXingRelation(doorWuxing, palaceWuxing); + + doorInfo.doorRelations.push({ + door, + palace: position, + palaceName, + relation, + doorWuxing, + palaceWuxing, + isHarmony: ['生', '比和'].includes(relation) + }); + } + } + + /** + * 根据地支获取值使门位置 + * @param {string} zhi - 地支 + * @returns {number} 值使门位置 + */ + getZhishiPositionByZhi(zhi) { + // 地支对应的宫位 + const zhiPositions = { + '子': 0, '丑': 1, '寅': 2, '卯': 2, + '辰': 3, '巳': 3, '午': 8, '未': 1, + '申': 6, '酉': 6, '戌': 7, '亥': 0 + }; + + return zhiPositions[zhi] || 0; + } + + /** + * 排布八神到地盘 + * @param {Array} dipan - 地盘数组 + * @param {boolean} yindun - 是否阴遁 + * @returns {Object} 八神排布信息 + */ + arrangeBaShen(dipan, yindun) { + const gods = ['值符', '螣蛇', '太阴', '六合', '白虎', '玄武', '九地', '九天']; + const godAttributes = ['吉', '凶', '吉', '吉', '凶', '凶', '吉', '吉']; + const godNatures = ['主', '辅', '阴', '和', '煞', '盗', '静', '动']; + + // 值符的位置(将在值符计算时确定,这里先用默认位置) + const zhifuPosition = 0; + + const godInfo = { + zhifuPosition, + godPositions: {}, + godRelations: [] + }; + + // 八宫位置映射(跳过中宫) + const palaceMapping = [0, 1, 2, 3, 5, 6, 7, 8]; + + // 排布八神 + for (let i = 0; i < 8; i++) { + let godIndex; + + if (yindun) { + // 阴遁:逆时针排列 + godIndex = (zhifuPosition - i + 8) % 8; + } else { + // 阳遁:顺时针排列 + godIndex = (zhifuPosition + i) % 8; + } + + const position = palaceMapping[godIndex]; + const god = gods[i]; + + if (dipan[position]) { + dipan[position].god = god; + dipan[position].godAttribute = godAttributes[i]; + dipan[position].godNature = godNatures[i]; + + // 记录神位信息 + godInfo.godPositions[god] = { + palace: position, + palaceName: this.getPalaceName(position), + attribute: godAttributes[i], + nature: godNatures[i], + isZhifu: i === 0 // 第一个神是值符 + }; + + // 分析神宫关系 + this.analyzeGodPalaceRelation(god, position, godInfo); + } + } + + // 中宫不排神 + if (dipan[4]) { + dipan[4].god = null; + dipan[4].godAttribute = null; + dipan[4].godNature = null; + } + + return godInfo; + } + + /** + * 分析神宫关系 + * @param {string} god - 神名 + * @param {number} position - 宫位 + * @param {Object} godInfo - 神位信息 + */ + analyzeGodPalaceRelation(god, position, godInfo) { + const palaceName = this.getPalaceName(position); + const godData = this.BASHEN[god]; + + if (godData) { + godInfo.godRelations.push({ + god, + palace: position, + palaceName, + attribute: godData.attribute || '中性', + nature: godData.nature || '平', + meaning: godData.meaning || '未知' + }); + } + } + + findShiganPosition(dipan, shigan) { + return dipan.findIndex(item => item.ganzhi === shigan); + } + + findZhizhiPosition(dipan, zhizhi) { + // 通过地支找到对应的天干位置 + // 地支与宫位的对应关系(传统奇门遁甲中的地支定位) + const zhizhiPalaceMap = { + '子': 0, // 坎一宫 + '丑': 7, // 艮八宫 + '寅': 7, // 艮八宫 + '卯': 2, // 震三宫 + '辰': 3, // 巽四宫 + '巳': 3, // 巽四宫 + '午': 8, // 离九宫 + '未': 1, // 坤二宫 + '申': 1, // 坤二宫 + '酉': 6, // 兑七宫 + '戌': 5, // 乾六宫 + '亥': 5 // 乾六宫 + }; + + // 首先尝试直接映射 + const directPosition = zhizhiPalaceMap[zhizhi]; + if (directPosition !== undefined) { + return directPosition; + } + + // 如果直接映射失败,在地盘中搜索包含该地支的位置 + for (let i = 0; i < dipan.length; i++) { + const palace = dipan[i]; + if (palace && palace.ganzhi) { + // 检查干支组合中是否包含目标地支 + if (palace.ganzhi.includes && palace.ganzhi.includes(zhizhi)) { + return i; + } + // 检查地支部分 + if (typeof palace.ganzhi === 'string' && palace.ganzhi.length >= 2) { + const zhi = palace.ganzhi.charAt(1); // 取第二个字符作为地支 + if (zhi === zhizhi) { + return i; + } + } + // 检查是否有单独的地支属性 + if (palace.zhi === zhizhi) { + return i; + } + } + } + + // 如果都找不到,返回默认位置(坎宫) + return 0; + } + + arrangeYindunTianpan(tianpan, dipan, shiganPosition) { + // 阴遁天盘排列 + for (let i = 0; i < 9; i++) { + tianpan[i] = { ...dipan[(shiganPosition + i) % 9] }; + } + } + + arrangeYangdunTianpan(tianpan, dipan, shiganPosition) { + // 阳遁天盘排列 + for (let i = 0; i < 9; i++) { + tianpan[i] = { ...dipan[(shiganPosition + i) % 9] }; + } + } + + analyzeSanQiPatterns(qimenPan) { + // 分析三奇格局 + return []; + } + + analyzeLiuYiPatterns(qimenPan) { + // 分析六仪格局 + return []; + } + + analyzeSpecialPatterns(qimenPan) { + // 分析特殊格局 + return []; + } + + findYongShenPosition(yongshen, qimenPan) { + // 找到用神在盘中的位置 + return 0; + } + + calculateWangShui(element, timeInfo) { + // 获取元素五行 + const elementWuxing = this.getGanZhiWuXing(element); + if (!elementWuxing) { + return '休'; // 无法确定五行时返回休 + } + + // 根据节气计算旺衰 + const jieqi = timeInfo.jieqi || this.solarTerms.getCurrentSolarTerm(new Date()).name; + + // 节气对应的五行旺衰 + const seasonalWangshui = { + // 春季 - 木旺火相土死金囚水休 + '立春': { '木': '旺', '火': '相', '土': '死', '金': '囚', '水': '休' }, + '雨水': { '木': '旺', '火': '相', '土': '死', '金': '囚', '水': '休' }, + '惊蛰': { '木': '旺', '火': '相', '土': '死', '金': '囚', '水': '休' }, + '春分': { '木': '旺', '火': '相', '土': '死', '金': '囚', '水': '休' }, + '清明': { '木': '旺', '火': '相', '土': '死', '金': '囚', '水': '休' }, + '谷雨': { '木': '旺', '火': '相', '土': '死', '金': '囚', '水': '休' }, + + // 夏季 - 火旺土相金死水囚木休 + '立夏': { '火': '旺', '土': '相', '金': '死', '水': '囚', '木': '休' }, + '小满': { '火': '旺', '土': '相', '金': '死', '水': '囚', '木': '休' }, + '芒种': { '火': '旺', '土': '相', '金': '死', '水': '囚', '木': '休' }, + '夏至': { '火': '旺', '土': '相', '金': '死', '水': '囚', '木': '休' }, + '小暑': { '火': '旺', '土': '相', '金': '死', '水': '囚', '木': '休' }, + '大暑': { '火': '旺', '土': '相', '金': '死', '水': '囚', '木': '休' }, + + // 秋季 - 金旺水相木死火囚土休 + '立秋': { '金': '旺', '水': '相', '木': '死', '火': '囚', '土': '休' }, + '处暑': { '金': '旺', '水': '相', '木': '死', '火': '囚', '土': '休' }, + '白露': { '金': '旺', '水': '相', '木': '死', '火': '囚', '土': '休' }, + '秋分': { '金': '旺', '水': '相', '木': '死', '火': '囚', '土': '休' }, + '寒露': { '金': '旺', '水': '相', '木': '死', '火': '囚', '土': '休' }, + '霜降': { '金': '旺', '水': '相', '木': '死', '火': '囚', '土': '休' }, + + // 冬季 - 水旺木相火死土囚金休 + '立冬': { '水': '旺', '木': '相', '火': '死', '土': '囚', '金': '休' }, + '小雪': { '水': '旺', '木': '相', '火': '死', '土': '囚', '金': '休' }, + '大雪': { '水': '旺', '木': '相', '火': '死', '土': '囚', '金': '休' }, + '冬至': { '水': '旺', '木': '相', '火': '死', '土': '囚', '金': '休' }, + '小寒': { '水': '旺', '木': '相', '火': '死', '土': '囚', '金': '休' }, + '大寒': { '水': '旺', '木': '相', '火': '死', '土': '囚', '金': '休' } + }; + + const jieqiData = seasonalWangshui[jieqi]; + if (jieqiData && jieqiData[elementWuxing]) { + return jieqiData[elementWuxing]; + } + + // 默认返回休 + return '休'; + } + + evaluateYongShenStatus(position, qimenPan) { + if (position === -1) { + return '未知'; + } + + const { dipan, tianpan, timeInfo } = qimenPan; + const palace = dipan[position]; + + if (!palace) { + return '未知'; + } + + let score = 0; + let factors = []; + + // 1. 检查宫位内的星门神组合 + const star = palace.star; + const door = palace.door; + const god = palace.god; + + // 吉星加分 + const auspiciousStars = ['天任', '天辅', '天心']; + const inauspiciousStars = ['天蓬', '天冲', '天柱', '天芮']; + + if (auspiciousStars.includes(star)) { + score += 20; + factors.push(`${star}为吉星`); + } else if (inauspiciousStars.includes(star)) { + score -= 15; + factors.push(`${star}为凶星`); + } + + // 吉门加分 + const auspiciousDoors = ['开门', '休门', '生门']; + const inauspiciousDoors = ['死门', '惊门', '伤门']; + + if (auspiciousDoors.includes(door)) { + score += 15; + factors.push(`${door}为吉门`); + } else if (inauspiciousDoors.includes(door)) { + score -= 12; + factors.push(`${door}为凶门`); + } + + // 吉神加分 + const auspiciousGods = ['值符', '六合', '太阴']; + const inauspiciousGods = ['白虎', '螣蛇', '朱雀']; + + if (auspiciousGods.includes(god)) { + score += 10; + factors.push(`${god}为吉神`); + } else if (inauspiciousGods.includes(god)) { + score -= 8; + factors.push(`${god}为凶神`); + } + + // 2. 检查宫位本身的吉凶 + const palaceInfo = this.JIUGONG[position + 1]; + if (palaceInfo) { + // 特殊宫位 + if (position === 4) { // 中宫 + score += 5; + factors.push('居中宫,得中正之气'); + } else if ([0, 2, 6, 8].includes(position)) { // 四正宫 + score += 3; + factors.push('居四正宫,位置较佳'); + } + } + + // 3. 检查时间因素 + const hourZhi = timeInfo.hour.zhi; + const auspiciousHours = ['子', '卯', '午', '酉']; + if (auspiciousHours.includes(hourZhi)) { + score += 5; + factors.push('时辰有利'); + } + + // 4. 检查节气因素 + const jieqi = timeInfo.jieqi; + const majorSolarTerms = ['春分', '夏至', '秋分', '冬至', '立春', '立夏', '立秋', '立冬']; + if (majorSolarTerms.includes(jieqi)) { + score += 3; + factors.push('节气有利'); + } + + // 5. 综合评估 + let status, description; + if (score >= 30) { + status = '极佳'; + description = '用神状态极佳,各方面条件都很有利'; + } else if (score >= 20) { + status = '很好'; + description = '用神状态很好,大部分条件有利'; + } else if (score >= 10) { + status = '良好'; + description = '用神状态良好,条件较为有利'; + } else if (score >= 0) { + status = '一般'; + description = '用神状态一般,需要谨慎行事'; + } else if (score >= -10) { + status = '较差'; + description = '用神状态较差,存在不利因素'; + } else { + status = '很差'; + description = '用神状态很差,多数条件不利'; + } + + return { + status, + score, + description, + factors, + palace: { + position, + star, + door, + god, + palaceName: this.JIUGONG[position + 1]?.name || '未知宫' + } + }; + } + + generateDetailedAnalysis(yongShenAnalysis, patterns) { + // 生成详细分析 + return ['用神状态良好', '格局组合有利']; + } + + generateSuggestions(yongShenAnalysis, qimenPan, question) { + // 生成建议 + return ['把握时机', '积极行动']; + } + + calculateTiming(yongShenAnalysis, qimenPan) { + // 计算应期 + return { + bestTime: '近期', + avoidTime: '无特别禁忌' + }; + } + + getOpponentYongshen(rigan) { + // 获取对手用神 + const opposites = { + '甲': '庚', '乙': '辛', '丙': '壬', '丁': '癸', '戊': '甲', + '己': '乙', '庚': '甲', '辛': '乙', '壬': '丙', '癸': '丁' + }; + return opposites[rigan] || '庚'; + } + + analyzePanStructure(qimenPan) { + // 分析盘面结构 + return { + overall: '结构平衡', + strengths: ['用神得力'], + weaknesses: ['需注意时机'] + }; + } + + analyzeWuXing(qimenPan) { + // 分析五行 + return { + dominant: '金', + balance: '较为平衡', + suggestions: ['注意水火调和'] + }; + } + + analyzeTimingFactors(qimenPan, currentTime) { + // 分析时间因素 + return { + season: '春季', + favorability: '有利', + notes: ['春季木旺,利于发展'] + }; + } +} + +// 奇门计算器类 +class QimenCalculator { + constructor(analyzer) { + this.analyzer = analyzer; + } + + // 计算完整的奇门盘 + calculateQimenPan(datetime) { + const timeInfo = this.calculateTimeInfo(datetime); + const dipan = this.calculateDiPan(timeInfo); + const tianpan = this.calculateTianPan(dipan, timeInfo); + const { zhifu, zhishi } = this.calculateZhiFuZhiShi(dipan, tianpan, timeInfo); + + return { + timeInfo, + dipan, + tianpan, + zhifu, + zhishi, + jushu: timeInfo.jushu, + yindun: timeInfo.yindun + }; + } + + /** + * 计算时间信息 + * @param {Date} datetime - 日期时间 + * @returns {Object} 时间信息 + */ + calculateTimeInfo(datetime) { + const year = datetime.getFullYear(); + const month = datetime.getMonth() + 1; + const day = datetime.getDate(); + const hour = datetime.getHours(); + + // 转换为干支 + const yearGZ = this.analyzer.getYearGanZhi(year); + const monthGZ = this.analyzer.getMonthGanZhi(year, month); + const dayGZ = this.analyzer.getDayGanZhi(datetime); + const hourGZ = this.analyzer.getHourGanZhi(hour, dayGZ.gan); + + // 计算节气 + const jieqi = this.analyzer.calculateJieQi(datetime); + const yuan = this.analyzer.calculateYuan(datetime, jieqi); + const { jushu, yindun } = this.analyzer.calculateJuShu(jieqi, yuan); + + return { + year: yearGZ, + month: monthGZ, + day: dayGZ, + hour: hourGZ, + jieqi: jieqi.name, + yuan, + jushu, + yindun + }; + } + + /** + * 计算地盘 + * @param {Object} timeInfo - 时间信息 + * @returns {Array} 地盘数组 + */ + calculateDiPan(timeInfo) { + const dipan = new Array(9).fill(null).map(() => ({ + ganzhi: null, + star: null, + door: null, + god: null + })); + + // 排三奇六仪 + const arrangementInfo = this.analyzer.arrangeSanQiLiuYi(dipan, timeInfo.jushu, timeInfo.yindun); + + // 保存排布信息到时间信息中 + timeInfo.arrangementInfo = arrangementInfo; + + // 排九星 + const starInfo = this.analyzer.arrangeJiuXing(dipan, timeInfo.jushu, timeInfo.yindun); + timeInfo.starInfo = starInfo; + + // 排八门 + const doorInfo = this.analyzer.arrangeBaMen(dipan, timeInfo.hour.zhi, timeInfo.yindun); + timeInfo.doorInfo = doorInfo; + + // 排八神 + const godInfo = this.analyzer.arrangeBaShen(dipan, timeInfo.yindun); + timeInfo.godInfo = godInfo; + + return dipan; + } + + /** + * 计算天盘 + * @param {Array} dipan - 地盘数组 + * @param {Object} timeInfo - 时间信息 + * @returns {Array} 天盘数组 + */ + calculateTianPan(dipan, timeInfo) { + const tianpan = new Array(9).fill(null).map(() => ({ + ganzhi: null, + star: null, + door: null, + god: null, + tianpanInfo: { + originalPosition: null, + isFeigong: false, + feigongType: null + } + })); + + // 找到时干在地盘的位置(值符位置) + const shiganPosition = this.findShiganPosition(dipan, timeInfo.hour.gan); + const zhifuStar = dipan[shiganPosition]?.star; + + // 找到值符星在地盘的原始位置 + const zhifuOriginalPosition = this.findStarOriginalPosition(zhifuStar, timeInfo.starInfo); + + // 计算天盘转换信息 + const tianpanInfo = { + shiganPosition, + zhifuStar, + zhifuOriginalPosition, + rotationOffset: this.calculateRotationOffset(shiganPosition, zhifuOriginalPosition, timeInfo.yindun), + transformations: [] + }; + + // 按阴阳遁规律排列天盘 + if (timeInfo.yindun) { + this.arrangeYindunTianpan(tianpan, dipan, tianpanInfo); + } else { + this.arrangeYangdunTianpan(tianpan, dipan, tianpanInfo); + } + + // 记录天盘信息 + timeInfo.tianpanInfo = tianpanInfo; + + return tianpan; + } + + /** + * 计算旋转偏移量 + * @param {number} shiganPosition - 时干位置 + * @param {number} zhifuOriginalPosition - 值符原始位置 + * @param {boolean} yindun - 是否阴遁 + * @returns {number} 旋转偏移量 + */ + calculateRotationOffset(shiganPosition, zhifuOriginalPosition, yindun) { + if (zhifuOriginalPosition === -1) { + return 0; // 无法确定原始位置,不旋转 + } + + let offset; + if (yindun) { + // 阴遁:逆时针旋转 + offset = (zhifuOriginalPosition - shiganPosition + 9) % 9; + } else { + // 阳遁:顺时针旋转 + offset = (shiganPosition - zhifuOriginalPosition + 9) % 9; + } + + return offset; + } + + /** + * 找到星的原始位置 + * @param {string} star - 星名 + * @param {Object} starInfo - 星位信息 + * @returns {number} 原始位置,-1表示未找到 + */ + findStarOriginalPosition(star, starInfo) { + if (!starInfo || !starInfo.starPositions[star]) { + return -1; + } + + // 星的原始位置就是其在地盘的位置 + return starInfo.starPositions[star].palace; + } + + /** + * 计算值符值使 + * @param {Array} dipan - 地盘数组 + * @param {Array} tianpan - 天盘数组 + * @param {Object} timeInfo - 时间信息 + * @returns {Object} 值符值使信息 + */ + calculateZhiFuZhiShi(dipan, tianpan, timeInfo) { + // 找到时干在地盘的位置 + const shiganPosition = this.findShiganPosition(dipan, timeInfo.hour.gan); + const zhifuStar = dipan[shiganPosition]?.star; + + // 找到时支对应的门 + const zhishiDoor = this.findZhishiDoor(timeInfo.hour.zhi, timeInfo.doorInfo); + + // 计算值符值使的详细信息 + const zhifuzhishiInfo = { + zhifu: { + star: zhifuStar, + position: shiganPosition, + palace: this.analyzer.getPalaceName(shiganPosition), + ganzhi: timeInfo.hour.gan, + isFeigong: this.checkFeigong(zhifuStar, shiganPosition, timeInfo.starInfo) + }, + zhishi: { + door: zhishiDoor, + position: this.findDoorPosition(zhishiDoor, dipan), + palace: this.analyzer.getPalaceName(this.findDoorPosition(zhishiDoor, dipan)), + zhi: timeInfo.hour.zhi, + isFeigong: this.checkDoorFeigong(zhishiDoor, timeInfo.doorInfo) + }, + relationship: this.analyzeZhifuZhishiRelation(zhifuStar, zhishiDoor) + }; + + // 更新星位和门位的值符值使标记 + this.updateZhifuZhishiMarks(timeInfo, zhifuzhishiInfo); + + return { + zhifu: zhifuStar, + zhishi: zhishiDoor, + details: zhifuzhishiInfo + }; + } + + /** + * 检查值符是否飞宫 + * @param {string} zhifuStar - 值符星 + * @param {number} position - 当前位置 + * @param {Object} starInfo - 星位信息 + * @returns {boolean} 是否飞宫 + */ + checkFeigong(zhifuStar, position, starInfo) { + if (!starInfo || !starInfo.starPositions[zhifuStar]) { + return false; + } + + const originalPosition = starInfo.starPositions[zhifuStar].palace; + return originalPosition !== position; + } + + /** + * 检查值使门是否飞宫 + * @param {string} zhishiDoor - 值使门 + * @param {Object} doorInfo - 门位信息 + * @returns {boolean} 是否飞宫 + */ + checkDoorFeigong(zhishiDoor, doorInfo) { + if (!doorInfo || !doorInfo.doorPositions[zhishiDoor]) { + return false; + } + + // 检查门是否在其本宫位置 + const doorPosition = doorInfo.doorPositions[zhishiDoor].palace; + const expectedPosition = this.getDoorOriginalPosition(zhishiDoor); + + return expectedPosition !== -1 && expectedPosition !== doorPosition; + } + + /** + * 获取门的原始宫位 + * @param {string} door - 门名 + * @returns {number} 原始宫位,-1表示无固定宫位 + */ + getDoorOriginalPosition(door) { + const doorOriginalPositions = { + '休门': 0, // 坎宫 + '死门': 1, // 坤宫 + '伤门': 2, // 震宫 + '杜门': 3, // 巽宫 + '景门': 8, // 离宫 + '惊门': 5, // 乾宫 + '开门': 6, // 兑宫 + '生门': 7 // 艮宫 + }; + + return doorOriginalPositions[door] !== undefined ? doorOriginalPositions[door] : -1; + } + + /** + * 找到值使门 + * @param {string} hourZhi - 时支 + * @param {Object} doorInfo - 门位信息 + * @returns {string} 值使门 + */ + findZhishiDoor(hourZhi, doorInfo) { + if (doorInfo && doorInfo.zhishiDoor) { + return doorInfo.zhishiDoor; + } + + // 根据时支确定值使门(简化处理) + const zhishiMapping = { + '子': '休门', '丑': '死门', '寅': '伤门', '卯': '杜门', + '辰': '景门', '巳': '惊门', '午': '开门', '未': '生门', + '申': '休门', '酉': '死门', '戌': '伤门', '亥': '杜门' + }; + + return zhishiMapping[hourZhi] || '休门'; + } + + /** + * 找到门在地盘的位置 + * @param {string} door - 门名 + * @param {Array} dipan - 地盘数组 + * @returns {number} 门的位置 + */ + findDoorPosition(door, dipan) { + for (let i = 0; i < dipan.length; i++) { + if (dipan[i] && dipan[i].door === door) { + return i; + } + } + return -1; + } + + /** + * 分析值符值使关系 + * @param {string} zhifuStar - 值符星 + * @param {string} zhishiDoor - 值使门 + * @returns {Object} 关系分析 + */ + analyzeZhifuZhishiRelation(zhifuStar, zhishiDoor) { + const starWuxing = this.analyzer.JIUXING[zhifuStar]?.element; + const doorWuxing = this.analyzer.BAMEN[zhishiDoor]?.element; + + if (starWuxing && doorWuxing) { + const relation = this.analyzer.getWuXingRelation(starWuxing, doorWuxing); + + return { + starWuxing, + doorWuxing, + relation, + isHarmony: ['生', '比和'].includes(relation), + description: this.getZhifuZhishiDescription(relation) + }; + } + + return { + relation: '未知', + isHarmony: false, + description: '无法确定关系' + }; + } + + /** + * 获取值符值使关系描述 + * @param {string} relation - 五行关系 + * @returns {string} 关系描述 + */ + getZhifuZhishiDescription(relation) { + const descriptions = { + '生': '值符生值使,主动有力,事情顺利', + '被生': '值使生值符,得助有力,贵人相助', + '克': '值符克值使,主动克制,需要努力', + '被克': '值使克值符,受制约束,阻力较大', + '比和': '值符值使同气,和谐一致,平稳发展' + }; + + return descriptions[relation] || '关系复杂,需综合判断'; + } + + /** + * 更新值符值使标记 + * @param {Object} timeInfo - 时间信息 + * @param {Object} zhifuzhishiInfo - 值符值使信息 + */ + updateZhifuZhishiMarks(timeInfo, zhifuzhishiInfo) { + // 更新星位的值符标记 + if (timeInfo.starInfo && timeInfo.starInfo.starPositions[zhifuzhishiInfo.zhifu.star]) { + timeInfo.starInfo.starPositions[zhifuzhishiInfo.zhifu.star].isZhifu = true; + } + + // 更新门位的值使标记 + if (timeInfo.doorInfo && timeInfo.doorInfo.doorPositions[zhifuzhishiInfo.zhishi.door]) { + timeInfo.doorInfo.doorPositions[zhifuzhishiInfo.zhishi.door].isZhishi = true; + } + } + + /** + * 找到时干在地盘的位置 + * @param {Array} dipan - 地盘数组 + * @param {string} shigan - 时干 + * @returns {number} 位置索引 + */ + findShiganPosition(dipan, shigan) { + for (let i = 0; i < dipan.length; i++) { + if (dipan[i].ganzhi === shigan) { + return i; + } + } + return 0; // 默认返回坎宫 + } + + /** + * 找到时支对应的位置 + * @param {Array} dipan - 地盘数组 + * @param {string} zhizhi - 时支 + * @returns {number} 位置索引 + */ + findZhizhiPosition(dipan, zhizhi) { + // 通过时支找到对应的天干位置(六甲遁法) + const liujiaXun = this.analyzer.LIUJIA_XUN; + + for (const [xunShou, xunData] of Object.entries(liujiaXun)) { + if (!xunData.empty.includes(zhizhi)) { + // 找到对应的六仪 + const liuyi = xunData.liuyi; + for (let i = 0; i < dipan.length; i++) { + if (dipan[i].ganzhi === liuyi) { + return i; + } + } + } + } + + return 0; // 默认返回坎宫 + } + + /** + * 阳遁天盘排列 + * @param {Array} tianpan - 天盘数组 + * @param {Array} dipan - 地盘数组 + * @param {Object} tianpanInfo - 天盘信息 + */ + arrangeYangdunTianpan(tianpan, dipan, tianpanInfo) { + const { shiganPosition, rotationOffset } = tianpanInfo; + + // 阳遁:根据值符位置进行顺时针转换 + for (let i = 0; i < 9; i++) { + // 计算源位置(考虑旋转偏移) + const sourcePos = (i + rotationOffset) % 9; + + // 复制地盘信息到天盘 + tianpan[i] = { + ganzhi: dipan[sourcePos]?.ganzhi || null, + star: dipan[sourcePos]?.star || null, + door: dipan[sourcePos]?.door || null, + god: dipan[sourcePos]?.god || null, + tianpanInfo: { + originalPosition: sourcePos, + isFeigong: sourcePos !== i, + feigongType: this.getFeigongType(sourcePos, i), + transformation: `地盘${sourcePos}宫 → 天盘${i}宫` + } + }; + + // 记录转换信息 + tianpanInfo.transformations.push({ + tianpanPosition: i, + dipanPosition: sourcePos, + isFeigong: sourcePos !== i, + element: dipan[sourcePos]?.ganzhi || dipan[sourcePos]?.star || dipan[sourcePos]?.door + }); + } + } + + /** + * 阴遁天盘排列 + * @param {Array} tianpan - 天盘数组 + * @param {Array} dipan - 地盘数组 + * @param {Object} tianpanInfo - 天盘信息 + */ + arrangeYindunTianpan(tianpan, dipan, tianpanInfo) { + const { shiganPosition, rotationOffset } = tianpanInfo; + + // 阴遁:根据值符位置进行逆时针转换 + for (let i = 0; i < 9; i++) { + // 计算源位置(考虑旋转偏移,阴遁逆向) + const sourcePos = (i - rotationOffset + 9) % 9; + + // 复制地盘信息到天盘 + tianpan[i] = { + ganzhi: dipan[sourcePos]?.ganzhi || null, + star: dipan[sourcePos]?.star || null, + door: dipan[sourcePos]?.door || null, + god: dipan[sourcePos]?.god || null, + tianpanInfo: { + originalPosition: sourcePos, + isFeigong: sourcePos !== i, + feigongType: this.getFeigongType(sourcePos, i), + transformation: `地盘${sourcePos}宫 → 天盘${i}宫` + } + }; + + // 记录转换信息 + tianpanInfo.transformations.push({ + tianpanPosition: i, + dipanPosition: sourcePos, + isFeigong: sourcePos !== i, + element: dipan[sourcePos]?.ganzhi || dipan[sourcePos]?.star || dipan[sourcePos]?.door + }); + } + } + + /** + * 获取飞宫类型 + * @param {number} originalPos - 原始位置 + * @param {number} currentPos - 当前位置 + * @returns {string} 飞宫类型 + */ + getFeigongType(originalPos, currentPos) { + if (originalPos === currentPos) { + return '伏吟'; // 不动 + } + + // 计算位置关系 + const diff = Math.abs(originalPos - currentPos); + const oppositePairs = [[0, 8], [1, 7], [2, 6], [3, 5]]; // 对冲宫位 + + // 检查是否为对冲(反吟) + for (const [pos1, pos2] of oppositePairs) { + if ((originalPos === pos1 && currentPos === pos2) || + (originalPos === pos2 && currentPos === pos1)) { + return '反吟'; + } + } + + // 其他情况为飞宫 + if (diff === 1 || diff === 8) { + return '相邻飞宫'; + } else if (diff === 2 || diff === 7) { + return '隔一飞宫'; + } else { + return '远距飞宫'; + } + } +} + +// 格局分析器类 +class PatternAnalyzer { + constructor(analyzer) { + this.analyzer = analyzer; + } + + analyzePatterns(qimenPan) { + const patterns = []; + + // 分析三奇格局 + patterns.push(...this.analyzeSanQiPatterns(qimenPan)); + + // 分析六仪格局 + patterns.push(...this.analyzeLiuYiPatterns(qimenPan)); + + // 分析特殊格局 + patterns.push(...this.analyzeSpecialPatterns(qimenPan)); + + // 分析组合格局 + patterns.push(...this.analyzeCombinationPatterns(qimenPan)); + + // 分析飞宫格局 + patterns.push(...this.analyzeFeigongPatterns(qimenPan)); + + // 按重要性排序 + return this.sortPatternsByImportance(patterns); + } + + /** + * 分析三奇格局 + * @param {Object} qimenPan - 奇门盘 + * @returns {Array} 三奇格局列表 + */ + analyzeSanQiPatterns(qimenPan) { + const patterns = []; + const { dipan, tianpan, timeInfo } = qimenPan; + + // 三奇:乙丙丁 + const sanqi = ['乙', '丙', '丁']; + const sanqiNames = { '乙': '日奇', '丙': '月奇', '丁': '星奇' }; + + for (const qi of sanqi) { + const qiPosition = this.findGanzhiPosition(qi, dipan); + if (qiPosition === -1) continue; + + const palace = qiPosition; + const palaceName = this.analyzer.getPalaceName(palace); + const star = dipan[palace]?.star; + const door = dipan[palace]?.door; + const god = dipan[palace]?.god; + + // 检查三奇得奇门格局 + if (this.isAuspiciousDoor(door)) { + patterns.push({ + name: `${sanqiNames[qi]}得奇门`, + type: '三奇格局', + level: '吉', + score: 20, + palace, + palaceName, + elements: { qi, star, door, god }, + description: `${sanqiNames[qi]}临${door},主事情顺利,贵人相助` + }); + } + + // 检查三奇升殿格局 + if (this.isSanqiShengdian(qi, palace)) { + patterns.push({ + name: `${sanqiNames[qi]}升殿`, + type: '三奇格局', + level: '大吉', + score: 25, + palace, + palaceName, + elements: { qi, star, door, god }, + description: `${sanqiNames[qi]}居${palaceName},得地得时,大吉之象` + }); + } + + // 检查三奇入墓格局 + if (this.isSanqiRumu(qi, palace)) { + patterns.push({ + name: `${sanqiNames[qi]}入墓`, + type: '三奇格局', + level: '凶', + score: -15, + palace, + palaceName, + elements: { qi, star, door, god }, + description: `${sanqiNames[qi]}入${palaceName},受困受制,不利发展` + }); + } + } + + return patterns; + } + + /** + * 分析六仪格局 + * @param {Object} qimenPan - 奇门盘 + * @returns {Array} 六仪格局列表 + */ + analyzeLiuYiPatterns(qimenPan) { + const patterns = []; + const { dipan, tianpan, timeInfo } = qimenPan; + + // 检查经典六仪格局 + const liuyiPatterns = { + '戊加己': { name: '青龙返首', level: '吉', score: 12, desc: '主田土房产大利' }, + '戊加庚': { name: '太白入荧', level: '凶', score: -12, desc: '主争斗官司' }, + '戊加辛': { name: '青龙折足', level: '凶', score: -10, desc: '主破财疾病' }, + '己加戊': { name: '贵人入狱', level: '凶', score: -8, desc: '主贵人受困' }, + '己加癸': { name: '地网高张', level: '凶', score: -15, desc: '主牢狱困顿' }, + '庚加乙': { name: '太白逢星', level: '吉', score: 10, desc: '主官司得理' }, + '庚加丁': { name: '亭亭之格', level: '吉', score: 15, desc: '主婚姻合作' } + }; + + // 检查天盘地盘组合 + for (let i = 0; i < 9; i++) { + const tianGan = tianpan[i]?.ganzhi; + const diGan = dipan[i]?.ganzhi; + + if (tianGan && diGan) { + const combination = `${tianGan}加${diGan}`; + const patternInfo = liuyiPatterns[combination]; + + if (patternInfo) { + patterns.push({ + name: patternInfo.name, + type: '六仪格局', + level: patternInfo.level, + score: patternInfo.score, + palace: i, + palaceName: this.analyzer.getPalaceName(i), + elements: { + tianGan, + diGan, + star: dipan[i]?.star, + door: dipan[i]?.door, + god: dipan[i]?.god + }, + description: `${tianGan}加${diGan},${patternInfo.desc}` + }); + } + } + } + + return patterns; + } + + /** + * 分析特殊格局 + * @param {Object} qimenPan - 奇门盘 + * @returns {Array} 特殊格局列表 + */ + analyzeSpecialPatterns(qimenPan) { + const patterns = []; + const { dipan, tianpan, timeInfo } = qimenPan; + + // 检查伏吟反吟格局 + let fuyinCount = 0; + let fanyinCount = 0; + + for (let i = 0; i < 9; i++) { + const tianInfo = tianpan[i]?.tianpanInfo; + if (tianInfo) { + if (tianInfo.feigongType === '伏吟') { + fuyinCount++; + } else if (tianInfo.feigongType === '反吟') { + fanyinCount++; + } + } + } + + if (fuyinCount >= 3) { + patterns.push({ + name: '伏吟格局', + type: '特殊格局', + level: '不利', + score: -5, + palace: -1, + palaceName: '全局', + elements: { count: fuyinCount }, + description: '多宫伏吟,主事情反复迟缓,不宜主动' + }); + } + + if (fanyinCount >= 3) { + patterns.push({ + name: '反吟格局', + type: '特殊格局', + level: '动荡', + score: -8, + palace: -1, + palaceName: '全局', + elements: { count: fanyinCount }, + description: '多宫反吟,主事情变化动荡,需谨慎应对' + }); + } + + // 检查值符飞宫格局 + if (timeInfo.tianpanInfo && timeInfo.tianpanInfo.rotationOffset !== 0) { + patterns.push({ + name: '值符飞宫', + type: '特殊格局', + level: '变化', + score: 0, + palace: timeInfo.tianpanInfo.shiganPosition, + palaceName: this.analyzer.getPalaceName(timeInfo.tianpanInfo.shiganPosition), + elements: { + zhifu: timeInfo.tianpanInfo.zhifuStar, + offset: timeInfo.tianpanInfo.rotationOffset + }, + description: '值符飞宫,主有变化升迁或职位调动' + }); + } + + return patterns; + } + + /** + * 分析组合格局 + * @param {Object} qimenPan - 奇门盘 + * @returns {Array} 组合格局列表 + */ + analyzeCombinationPatterns(qimenPan) { + const patterns = []; + const { dipan, tianpan, timeInfo } = qimenPan; + + // 检查星门组合格局 + for (let i = 0; i < 9; i++) { + const star = dipan[i]?.star; + const door = dipan[i]?.door; + + if (star && door) { + const combination = this.getStarDoorCombination(star, door); + if (combination) { + patterns.push({ + name: combination.name, + type: '星门组合', + level: combination.level, + score: combination.score, + palace: i, + palaceName: this.analyzer.getPalaceName(i), + elements: { star, door }, + description: combination.description + }); + } + } + } + + return patterns; + } + + /** + * 分析飞宫格局 + * @param {Object} qimenPan - 奇门盘 + * @returns {Array} 飞宫格局列表 + */ + analyzeFeigongPatterns(qimenPan) { + const patterns = []; + const { tianpan, timeInfo } = qimenPan; + + if (!timeInfo.tianpanInfo) return patterns; + + const { transformations } = timeInfo.tianpanInfo; + + // 分析重要的飞宫组合 + for (const transform of transformations) { + if (transform.isFeigong && transform.element) { + const feigongPattern = this.getFeigongPattern(transform); + if (feigongPattern) { + patterns.push(feigongPattern); + } + } + } + + return patterns; + } + + // 辅助方法 + findGanzhiPosition(ganzhi, dipan) { + for (let i = 0; i < dipan.length; i++) { + if (dipan[i]?.ganzhi === ganzhi) { + return i; + } + } + return -1; + } + + isAuspiciousDoor(door) { + return ['开门', '休门', '生门'].includes(door); + } + + isSanqiShengdian(qi, palace) { + const shengdianMap = { + '乙': [8], // 日奇升殿在离宫 + '丙': [8], // 月奇升殿在离宫 + '丁': [8] // 星奇升殿在离宫 + }; + return shengdianMap[qi]?.includes(palace) || false; + } + + isSanqiRumu(qi, palace) { + const rumuMap = { + '乙': [0], // 日奇入墓在坎宫 + '丙': [0], // 月奇入墓在坎宫 + '丁': [0] // 星奇入墓在坎宫 + }; + return rumuMap[qi]?.includes(palace) || false; + } + + getStarDoorCombination(star, door) { + const combinations = { + '天心开门': { name: '天心开门', level: '大吉', score: 25, description: '天心配开门,主升迁发达' }, + '天任生门': { name: '天任生门', level: '吉', score: 20, description: '天任配生门,主财源广进' }, + '天英景门': { name: '天英景门', level: '吉', score: 18, description: '天英配景门,主文书喜庆' } + }; + + const key = star + door; + return combinations[key] || null; + } + + getFeigongPattern(transform) { + // 根据飞宫情况判断格局 + if (this.analyzer.isSanQi(transform.element)) { + return { + name: '三奇飞宫', + type: '飞宫格局', + level: '变化', + score: 5, + palace: transform.tianpanPosition, + palaceName: this.analyzer.getPalaceName(transform.tianpanPosition), + elements: { element: transform.element }, + description: `${transform.element}飞宫,主有变动机遇` + }; + } + return null; + } + + sortPatternsByImportance(patterns) { + return patterns.sort((a, b) => { + // 按分数绝对值排序,分数高的在前 + return Math.abs(b.score) - Math.abs(a.score); + }); + } +} + +// 用神分析器类 +class YongShenAnalyzer { + constructor(analyzer) { + this.analyzer = analyzer; + } + + /** + * 智能选择用神 + * @param {string} question - 问题描述 + * @param {Object} birthData - 出生信息 + * @param {Object} qimenPan - 奇门盘 + * @returns {Object} 用神配置 + */ + selectYongShen(question, birthData, qimenPan) { + const questionType = this.classifyQuestion(question); + const rigan = qimenPan.timeInfo.day.gan; + const gender = birthData?.gender || '未知'; + + // 基础用神配置 + const yongshen = { + rigan, + questionType, + primary: {}, + secondary: {}, + auxiliary: {} + }; + + // 根据问题类型配置用神 + switch (questionType) { + case '婚姻': + yongshen.primary = this.getMarriageYongshen(gender, qimenPan); + yongshen.secondary = { + matchmaker: '六合', + marriage_palace: '兑宫', + relationship_door: '休门' + }; + break; + + case '求财': + yongshen.primary = this.getWealthYongshen(qimenPan); + yongshen.secondary = { + capital: '戊', + profit: '天任', + opportunity: '开门' + }; + break; + + case '疾病': + yongshen.primary = this.getHealthYongshen(qimenPan); + yongshen.secondary = { + doctor: '天心', + medicine: '乙', + recovery: '生门' + }; + break; + + case '官司': + yongshen.primary = this.getLawsuitYongshen(qimenPan); + yongshen.secondary = { + judge: '天心', + evidence: '丁', + justice: '开门' + }; + break; + + case '求职': + yongshen.primary = this.getCareerYongshen(qimenPan); + yongshen.secondary = { + boss: '天心', + opportunity: '开门', + success: '生门' + }; + break; + + case '学业': + yongshen.primary = this.getStudyYongshen(qimenPan); + yongshen.secondary = { + teacher: '天心', + wisdom: '丁', + exam: '景门' + }; + break; + + case '出行': + yongshen.primary = this.getTravelYongshen(qimenPan); + yongshen.secondary = { + safety: '九天', + direction: '值符', + timing: '开门' + }; + break; + + default: + yongshen.primary = { + matter: qimenPan.timeInfo.hour.gan, + result: qimenPan.zhishi + }; + yongshen.secondary = { + timing: '值符', + method: '值使' + }; + } + + // 添加辅助用神 + yongshen.auxiliary = { + zhifu: qimenPan.zhifu, + zhishi: qimenPan.zhishi, + season: this.analyzer.getCurrentSeason(new Date()), + dayMaster: rigan + }; + + return yongshen; + } + + /** + * 获取婚姻用神 + * @param {string} gender - 性别 + * @param {Object} qimenPan - 奇门盘 + * @returns {Object} 婚姻用神 + */ + getMarriageYongshen(gender, qimenPan) { + if (gender === '男') { + return { + self: '庚', // 男命以庚为自己 + spouse: '乙', // 以乙为妻子 + relationship: '六合' + }; + } else if (gender === '女') { + return { + self: '乙', // 女命以乙为自己 + spouse: '庚', // 以庚为丈夫 + relationship: '六合' + }; + } else { + return { + self: qimenPan.timeInfo.day.gan, + spouse: this.getSpouseGan(qimenPan.timeInfo.day.gan), + relationship: '六合' + }; + } + } + + /** + * 获取求财用神 + * @param {Object} qimenPan - 奇门盘 + * @returns {Object} 求财用神 + */ + getWealthYongshen(qimenPan) { + return { + wealth: '生门', // 生门主财源 + investment: '戊', // 戊为资本 + profit: '天任', // 天任主财利 + timing: '开门' // 开门主机会 + }; + } + + /** + * 获取健康用神 + * @param {Object} qimenPan - 奇门盘 + * @returns {Object} 健康用神 + */ + getHealthYongshen(qimenPan) { + return { + health: qimenPan.timeInfo.day.gan, // 日干为自身 + illness: '天芮', // 天芮主疾病 + treatment: '天心', // 天心主医疗 + recovery: '生门' // 生门主康复 + }; + } + + /** + * 获取官司用神 + * @param {Object} qimenPan - 奇门盘 + * @returns {Object} 官司用神 + */ + getLawsuitYongshen(qimenPan) { + return { + plaintiff: qimenPan.timeInfo.day.gan, // 日干为求测人 + defendant: this.getOpponentGan(qimenPan.timeInfo.day.gan), // 对方 + judge: '天心', // 天心为法官 + verdict: '开门' // 开门主判决 + }; + } + + /** + * 获取求职用神 + * @param {Object} qimenPan - 奇门盘 + * @returns {Object} 求职用神 + */ + getCareerYongshen(qimenPan) { + return { + self: qimenPan.timeInfo.day.gan, // 日干为自己 + job: '庚', // 庚为工作 + boss: '天心', // 天心为领导 + success: '开门' // 开门主成功 + }; + } + + /** + * 获取学业用神 + * @param {Object} qimenPan - 奇门盘 + * @returns {Object} 学业用神 + */ + getStudyYongshen(qimenPan) { + return { + student: qimenPan.timeInfo.day.gan, // 日干为学生 + knowledge: '丁', // 丁为文书学问 + teacher: '天心', // 天心为老师 + exam: '景门' // 景门主考试 + }; + } + + /** + * 获取出行用神 + * @param {Object} qimenPan - 奇门盘 + * @returns {Object} 出行用神 + */ + getTravelYongshen(qimenPan) { + return { + traveler: qimenPan.timeInfo.day.gan, // 日干为出行人 + journey: '九天', // 九天主远行 + safety: '太阴', // 太阴主平安 + destination: '开门' // 开门主目的地 + }; + } + + /** + * 获取配偶干支 + * @param {string} rigan - 日干 + * @returns {string} 配偶干支 + */ + getSpouseGan(rigan) { + const spouseMap = { + '甲': '己', '乙': '庚', '丙': '辛', '丁': '壬', '戊': '癸', + '己': '甲', '庚': '乙', '辛': '丙', '壬': '丁', '癸': '戊' + }; + return spouseMap[rigan] || '庚'; + } + + /** + * 获取对手干支 + * @param {string} rigan - 日干 + * @returns {string} 对手干支 + */ + getOpponentGan(rigan) { + // 以克我者为对手 + const opponentMap = { + '甲': '庚', '乙': '辛', '丙': '壬', '丁': '癸', '戊': '甲', + '己': '乙', '庚': '丙', '辛': '丁', '壬': '戊', '癸': '己' + }; + return opponentMap[rigan] || '庚'; + } + + /** + * 分析用神状态 + * @param {Object} yongshen - 用神配置 + * @param {Object} qimenPan - 奇门盘 + * @returns {Object} 用神分析结果 + */ + analyzeYongShen(yongshen, qimenPan) { + const analysis = { + primary: {}, + secondary: {}, + auxiliary: {}, + overall: { + favorability: 0, + strength: 0, + timing: '中等', + recommendation: '' + } + }; + + // 分析主用神 + if (yongshen.primary) { + for (const [key, value] of Object.entries(yongshen.primary)) { + if (typeof value === 'string') { + analysis.primary[key] = this.analyzeElementStatus(value, qimenPan); + } + } + } + + // 分析次用神 + if (yongshen.secondary) { + for (const [key, value] of Object.entries(yongshen.secondary)) { + if (typeof value === 'string') { + analysis.secondary[key] = this.analyzeElementStatus(value, qimenPan); + } + } + } + + // 分析辅助用神 + if (yongshen.auxiliary) { + for (const [key, value] of Object.entries(yongshen.auxiliary)) { + if (typeof value === 'string') { + analysis.auxiliary[key] = this.analyzeElementStatus(value, qimenPan); + } + } + } + + // 兼容旧格式 + for (const [key, value] of Object.entries(yongshen)) { + if (typeof value === 'string' && !['questionType', 'rigan'].includes(key)) { + if (!analysis.primary[key] && !analysis.secondary[key] && !analysis.auxiliary[key]) { + analysis.primary[key] = this.analyzeElementStatus(value, qimenPan); + } + } + } + + // 综合评估 + analysis.overall = this.calculateOverallAnalysis(analysis, qimenPan); + + return analysis; + } + + /** + * 查找元素在奇门盘中的位置 + * @param {string} element - 元素(干支、星、门、神) + * @param {Object} qimenPan - 奇门盘 + * @returns {number} 位置索引,-1表示未找到 + */ + findElementPosition(element, qimenPan) { + const { dipan } = qimenPan; + + // 查找干支 + for (let i = 0; i < dipan.length; i++) { + if (dipan[i]?.ganzhi === element) { + return i; + } + } + + // 查找九星 + for (let i = 0; i < dipan.length; i++) { + if (dipan[i]?.star === element) { + return i; + } + } + + // 查找八门 + for (let i = 0; i < dipan.length; i++) { + if (dipan[i]?.door === element) { + return i; + } + } + + // 查找八神 + for (let i = 0; i < dipan.length; i++) { + if (dipan[i]?.god === element) { + return i; + } + } + + return -1; + } + + /** + * 分析元素状态 + * @param {string} element - 元素(干支、星、门、神) + * @param {Object} qimenPan - 奇门盘 + * @returns {Object} 元素状态分析 + */ + analyzeElementStatus(element, qimenPan) { + const position = this.findElementPosition(element, qimenPan); + const wangshui = this.calculateWangShui(element, position, qimenPan); + const palaceRelation = this.analyzePalaceRelation(element, position, qimenPan); + const seasonalInfluence = this.analyzeSeasonalInfluence(element, qimenPan); + + // 综合评分 + const score = this.calculateElementScore(wangshui, palaceRelation, seasonalInfluence); + + return { + element, + position, + palaceName: position !== -1 ? this.analyzer.getPalaceName(position) : '未找到', + wangshui: wangshui.level, + wangshuiScore: wangshui.score, + palaceRelation: palaceRelation.relation, + palaceHarmony: palaceRelation.harmony, + seasonalInfluence: seasonalInfluence.influence, + seasonalScore: seasonalInfluence.score, + totalScore: score, + status: this.getStatusByScore(score), + description: this.generateElementDescription(element, wangshui, palaceRelation, seasonalInfluence) + }; + } + + classifyQuestion(question) { + const keywords = { + '婚姻': ['婚姻', '结婚', '恋爱', '感情'], + '求财': ['财运', '投资', '生意', '赚钱'], + '疾病': ['健康', '疾病', '治病', '身体'], + '官司': ['官司', '诉讼', '法律', '纠纷'], + '求职': ['工作', '求职', '面试', '职业'] + }; + + for (const [type, words] of Object.entries(keywords)) { + if (words.some(word => question.includes(word))) { + return type; + } + } + + return '其他'; + } + + findYongShenPosition(yongshen, qimenPan) { + const { dipan, tianpan } = qimenPan; + + // 如果用神是字符串,直接查找 + if (typeof yongshen === 'string') { + return this.findElementPosition(yongshen, qimenPan); + } + + // 如果用神是对象,查找主用神 + if (yongshen && typeof yongshen === 'object') { + // 查找主用神(primary) + if (yongshen.primary) { + for (const [key, value] of Object.entries(yongshen.primary)) { + if (typeof value === 'string') { + const position = this.findElementPosition(value, qimenPan); + if (position !== -1) { + return position; + } + } + } + } + + // 查找次用神(secondary) + if (yongshen.secondary) { + for (const [key, value] of Object.entries(yongshen.secondary)) { + if (typeof value === 'string') { + const position = this.findElementPosition(value, qimenPan); + if (position !== -1) { + return position; + } + } + } + } + + // 查找辅助用神(auxiliary) + if (yongshen.auxiliary) { + for (const [key, value] of Object.entries(yongshen.auxiliary)) { + if (typeof value === 'string') { + const position = this.findElementPosition(value, qimenPan); + if (position !== -1) { + return position; + } + } + } + } + } + + return -1; // 未找到 + } + + /** + * 计算旺衰 + * @param {string} element - 元素 + * @param {number} position - 位置 + * @param {Object} qimenPan - 奇门盘 + * @returns {Object} 旺衰分析 + */ + calculateWangShui(element, position, qimenPan) { + if (position === -1) { + return { level: '未知', score: 0, reason: '元素未找到' }; + } + + // 获取元素五行 + const elementWuxing = this.getElementWuxing(element); + if (!elementWuxing) { + return { level: '未知', score: 0, reason: '无法确定五行' }; + } + + // 基于节气的旺衰 + const seasonalWangshui = this.getSeasonalWangshui(elementWuxing, qimenPan); + + // 基于宫位的旺衰 + const palaceWangshui = this.getPalaceWangshui(elementWuxing, position); + + // 基于时间的旺衰 + const timeWangshui = this.getTimeWangshui(elementWuxing, qimenPan.timeInfo); + + // 综合计算 + const totalScore = seasonalWangshui.score + palaceWangshui.score + timeWangshui.score; + const level = this.getWangshuiLevel(totalScore); + + return { + level, + score: totalScore, + seasonal: seasonalWangshui, + palace: palaceWangshui, + time: timeWangshui, + reason: `节气${seasonalWangshui.level},宫位${palaceWangshui.level},时间${timeWangshui.level}` + }; + } + + getElementWuxing(element) { + // 先检查是否为干支 + const ganzhiWuxing = this.analyzer.getGanZhiWuXing(element); + if (ganzhiWuxing) return ganzhiWuxing; + + // 检查是否为九星 + const starWuxing = this.analyzer.JIUXING[element]?.element; + if (starWuxing) return starWuxing; + + // 检查是否为八门 + const doorWuxing = this.analyzer.BAMEN[element]?.element; + if (doorWuxing) return doorWuxing; + + // 检查是否为八神 + const godWuxing = this.analyzer.BASHEN[element]?.wuxing; + if (godWuxing) return godWuxing; + + return null; + } + + // 根据节气计算五行旺衰 + getSeasonalWangshui(wuxing, qimenPan) { + const { timeInfo } = qimenPan; + const jieqi = timeInfo.jieqi; + + // 节气对应的季节和五行旺衰关系 + const seasonalWangshui = { + // 春季 - 木旺火相土死金囚水休 + '立春': { '木': { level: '旺', score: 15 }, '火': { level: '相', score: 10 }, '土': { level: '死', score: -10 }, '金': { level: '囚', score: -5 }, '水': { level: '休', score: 0 } }, + '雨水': { '木': { level: '旺', score: 15 }, '火': { level: '相', score: 10 }, '土': { level: '死', score: -10 }, '金': { level: '囚', score: -5 }, '水': { level: '休', score: 0 } }, + '惊蛰': { '木': { level: '旺', score: 15 }, '火': { level: '相', score: 10 }, '土': { level: '死', score: -10 }, '金': { level: '囚', score: -5 }, '水': { level: '休', score: 0 } }, + '春分': { '木': { level: '旺', score: 15 }, '火': { level: '相', score: 10 }, '土': { level: '死', score: -10 }, '金': { level: '囚', score: -5 }, '水': { level: '休', score: 0 } }, + '清明': { '木': { level: '旺', score: 15 }, '火': { level: '相', score: 10 }, '土': { level: '死', score: -10 }, '金': { level: '囚', score: -5 }, '水': { level: '休', score: 0 } }, + '谷雨': { '木': { level: '旺', score: 15 }, '火': { level: '相', score: 10 }, '土': { level: '死', score: -10 }, '金': { level: '囚', score: -5 }, '水': { level: '休', score: 0 } }, + + // 夏季 - 火旺土相金死水囚木休 + '立夏': { '火': { level: '旺', score: 15 }, '土': { level: '相', score: 10 }, '金': { level: '死', score: -10 }, '水': { level: '囚', score: -5 }, '木': { level: '休', score: 0 } }, + '小满': { '火': { level: '旺', score: 15 }, '土': { level: '相', score: 10 }, '金': { level: '死', score: -10 }, '水': { level: '囚', score: -5 }, '木': { level: '休', score: 0 } }, + '芒种': { '火': { level: '旺', score: 15 }, '土': { level: '相', score: 10 }, '金': { level: '死', score: -10 }, '水': { level: '囚', score: -5 }, '木': { level: '休', score: 0 } }, + '夏至': { '火': { level: '旺', score: 15 }, '土': { level: '相', score: 10 }, '金': { level: '死', score: -10 }, '水': { level: '囚', score: -5 }, '木': { level: '休', score: 0 } }, + '小暑': { '火': { level: '旺', score: 15 }, '土': { level: '相', score: 10 }, '金': { level: '死', score: -10 }, '水': { level: '囚', score: -5 }, '木': { level: '休', score: 0 } }, + '大暑': { '火': { level: '旺', score: 15 }, '土': { level: '相', score: 10 }, '金': { level: '死', score: -10 }, '水': { level: '囚', score: -5 }, '木': { level: '休', score: 0 } }, + + // 秋季 - 金旺水相木死火囚土休 + '立秋': { '金': { level: '旺', score: 15 }, '水': { level: '相', score: 10 }, '木': { level: '死', score: -10 }, '火': { level: '囚', score: -5 }, '土': { level: '休', score: 0 } }, + '处暑': { '金': { level: '旺', score: 15 }, '水': { level: '相', score: 10 }, '木': { level: '死', score: -10 }, '火': { level: '囚', score: -5 }, '土': { level: '休', score: 0 } }, + '白露': { '金': { level: '旺', score: 15 }, '水': { level: '相', score: 10 }, '木': { level: '死', score: -10 }, '火': { level: '囚', score: -5 }, '土': { level: '休', score: 0 } }, + '秋分': { '金': { level: '旺', score: 15 }, '水': { level: '相', score: 10 }, '木': { level: '死', score: -10 }, '火': { level: '囚', score: -5 }, '土': { level: '休', score: 0 } }, + '寒露': { '金': { level: '旺', score: 15 }, '水': { level: '相', score: 10 }, '木': { level: '死', score: -10 }, '火': { level: '囚', score: -5 }, '土': { level: '休', score: 0 } }, + '霜降': { '金': { level: '旺', score: 15 }, '水': { level: '相', score: 10 }, '木': { level: '死', score: -10 }, '火': { level: '囚', score: -5 }, '土': { level: '休', score: 0 } }, + + // 冬季 - 水旺木相火死土囚金休 + '立冬': { '水': { level: '旺', score: 15 }, '木': { level: '相', score: 10 }, '火': { level: '死', score: -10 }, '土': { level: '囚', score: -5 }, '金': { level: '休', score: 0 } }, + '小雪': { '水': { level: '旺', score: 15 }, '木': { level: '相', score: 10 }, '火': { level: '死', score: -10 }, '土': { level: '囚', score: -5 }, '金': { level: '休', score: 0 } }, + '大雪': { '水': { level: '旺', score: 15 }, '木': { level: '相', score: 10 }, '火': { level: '死', score: -10 }, '土': { level: '囚', score: -5 }, '金': { level: '休', score: 0 } }, + '冬至': { '水': { level: '旺', score: 15 }, '木': { level: '相', score: 10 }, '火': { level: '死', score: -10 }, '土': { level: '囚', score: -5 }, '金': { level: '休', score: 0 } }, + '小寒': { '水': { level: '旺', score: 15 }, '木': { level: '相', score: 10 }, '火': { level: '死', score: -10 }, '土': { level: '囚', score: -5 }, '金': { level: '休', score: 0 } }, + '大寒': { '水': { level: '旺', score: 15 }, '木': { level: '相', score: 10 }, '火': { level: '死', score: -10 }, '土': { level: '囚', score: -5 }, '金': { level: '休', score: 0 } } + }; + + const jieqiData = seasonalWangshui[jieqi]; + if (jieqiData && jieqiData[wuxing]) { + return jieqiData[wuxing]; + } + + // 默认返回平和状态 + return { level: '休', score: 0 }; + } + + getPalaceWangshui(wuxing, position) { + // 九宫与五行的对应关系 + const palaceWuxing = this.analyzer.JIUGONG[position + 1]?.element; + if (!palaceWuxing) { + return { level: '休', score: 0 }; + } + + // 计算五行生克关系 + const relation = this.analyzer.getWuXingRelation(wuxing, palaceWuxing); + + let level, score; + switch (relation) { + case '生': // 我生宫位,泄气 + level = '泄'; + score = -8; + break; + case '被生': // 宫位生我,得气 + level = '旺'; + score = 12; + break; + case '克': // 我克宫位,耗气 + level = '耗'; + score = -5; + break; + case '被克': // 宫位克我,受制 + level = '囚'; + score = -12; + break; + case '比和': // 同类五行,和谐 + level = '旺'; + score = 10; + break; + default: + level = '休'; + score = 0; + } + + // 特殊宫位加成 + if (position === 4) { // 中宫 + score += 3; // 中宫得地利 + } + + return { level, score }; + } + + getTimeWangshui(wuxing, timeInfo) { + const { hour } = timeInfo; + const hourZhi = hour.zhi; + + // 时辰地支对应的五行 + const zhiWuxing = { + '子': '水', '丑': '土', '寅': '木', '卯': '木', + '辰': '土', '巳': '火', '午': '火', '未': '土', + '申': '金', '酉': '金', '戌': '土', '亥': '水' + }; + + const timeWuxing = zhiWuxing[hourZhi]; + if (!timeWuxing) { + return { level: '休', score: 0 }; + } + + // 计算与时辰五行的关系 + const relation = this.analyzer.getWuXingRelation(wuxing, timeWuxing); + + let level, score; + switch (relation) { + case '生': // 我生时辰,主动 + level = '相'; + score = 8; + break; + case '被生': // 时辰生我,得助 + level = '旺'; + score = 12; + break; + case '克': // 我克时辰,消耗 + level = '耗'; + score = -3; + break; + case '被克': // 时辰克我,受阻 + level = '囚'; + score = -8; + break; + case '比和': // 同类,和谐 + level = '旺'; + score = 10; + break; + default: + level = '休'; + score = 0; + } + + // 特殊时辰调整 + if (['子', '午', '卯', '酉'].includes(hourZhi)) { + // 四正时辰,力量较强 + score += 2; + } else if (['辰', '戌', '丑', '未'].includes(hourZhi)) { + // 四库时辰,蓄藏之力 + score += 1; + } + + return { level, score }; + } + + getWangshuiLevel(score) { + if (score >= 20) return '旺'; + if (score >= 10) return '相'; + if (score >= 0) return '休'; + if (score >= -10) return '囚'; + return '死'; + } + + analyzePalaceRelation(element, position, qimenPan) { + if (position === -1) { + return { relation: '未知', score: 0, harmony: false }; + } + + const { dipan, tianpan } = qimenPan; + const palace = dipan[position]; + const palaceInfo = this.analyzer.JIUGONG[position + 1]; + + if (!palace || !palaceInfo) { + return { relation: '未知', score: 0, harmony: false }; + } + + // 获取元素五行 + const elementWuxing = this.getElementWuxing(element); + const palaceWuxing = palaceInfo.element; + + if (!elementWuxing || !palaceWuxing) { + return { relation: '未知', score: 0, harmony: false }; + } + + // 计算五行关系 + const wuxingRelation = this.analyzer.getWuXingRelation(elementWuxing, palaceWuxing); + + let relation, score, harmony; + switch (wuxingRelation) { + case '生': + relation = '生助宫位'; + score = 8; + harmony = true; + break; + case '被生': + relation = '得宫位生助'; + score = 15; + harmony = true; + break; + case '克': + relation = '克制宫位'; + score = -5; + harmony = false; + break; + case '被克': + relation = '被宫位克制'; + score = -15; + harmony = false; + break; + case '比和': + relation = '与宫位和谐'; + score = 12; + harmony = true; + break; + default: + relation = '关系平和'; + score = 0; + harmony = true; + } + + // 检查宫位内其他元素的影响 + let additionalScore = 0; + + // 检查同宫的星、门、神 + if (palace.star && palace.star !== element) { + const starWuxing = this.analyzer.JIUXING[palace.star]?.element; + if (starWuxing) { + const starRelation = this.analyzer.getWuXingRelation(elementWuxing, starWuxing); + if (starRelation === '被生') additionalScore += 3; + else if (starRelation === '被克') additionalScore -= 3; + } + } + + if (palace.door && palace.door !== element) { + const doorWuxing = this.analyzer.BAMEN[palace.door]?.element; + if (doorWuxing) { + const doorRelation = this.analyzer.getWuXingRelation(elementWuxing, doorWuxing); + if (doorRelation === '被生') additionalScore += 2; + else if (doorRelation === '被克') additionalScore -= 2; + } + } + + // 特殊宫位加成 + if (position === 4) { // 中宫 + additionalScore += 2; // 中宫为中心,有利 + } + + const finalScore = score + additionalScore; + + return { + relation, + score: finalScore, + harmony, + wuxingRelation, + palaceElement: palaceWuxing, + elementWuxing, + additionalInfluence: additionalScore + }; + } + + analyzeSeasonalInfluence(element, qimenPan) { + const { timeInfo } = qimenPan; + const jieqi = timeInfo.jieqi; + + // 获取元素五行 + const elementWuxing = this.getElementWuxing(element); + if (!elementWuxing) { + return { influence: '未知', score: 0, season: '未知' }; + } + + // 节气对应的季节 + const seasonMap = { + '立春': '春', '雨水': '春', '惊蛰': '春', '春分': '春', '清明': '春', '谷雨': '春', + '立夏': '夏', '小满': '夏', '芒种': '夏', '夏至': '夏', '小暑': '夏', '大暑': '夏', + '立秋': '秋', '处暑': '秋', '白露': '秋', '秋分': '秋', '寒露': '秋', '霜降': '秋', + '立冬': '冬', '小雪': '冬', '大雪': '冬', '冬至': '冬', '小寒': '冬', '大寒': '冬' + }; + + const season = seasonMap[jieqi] || '未知'; + + // 五行与季节的关系 + const seasonalInfluence = { + '春': { + '木': { influence: '极为有利', score: 20, reason: '木旺于春,得时令之助' }, + '火': { influence: '较为有利', score: 12, reason: '木生火,春季火相' }, + '土': { influence: '极为不利', score: -15, reason: '木克土,春季土死' }, + '金': { influence: '不利', score: -8, reason: '金克木,春季金囚' }, + '水': { influence: '平和', score: 3, reason: '水生木,春季水休但有生助之功' } + }, + '夏': { + '火': { influence: '极为有利', score: 20, reason: '火旺于夏,得时令之助' }, + '土': { influence: '较为有利', score: 12, reason: '火生土,夏季土相' }, + '金': { influence: '极为不利', score: -15, reason: '火克金,夏季金死' }, + '水': { influence: '不利', score: -8, reason: '水克火,夏季水囚' }, + '木': { influence: '平和', score: 3, reason: '木生火,夏季木休但有生助之功' } + }, + '秋': { + '金': { influence: '极为有利', score: 20, reason: '金旺于秋,得时令之助' }, + '水': { influence: '较为有利', score: 12, reason: '金生水,秋季水相' }, + '木': { influence: '极为不利', score: -15, reason: '金克木,秋季木死' }, + '火': { influence: '不利', score: -8, reason: '火克金,秋季火囚' }, + '土': { influence: '平和', score: 3, reason: '土生金,秋季土休但有生助之功' } + }, + '冬': { + '水': { influence: '极为有利', score: 20, reason: '水旺于冬,得时令之助' }, + '木': { influence: '较为有利', score: 12, reason: '水生木,冬季木相' }, + '火': { influence: '极为不利', score: -15, reason: '水克火,冬季火死' }, + '土': { influence: '不利', score: -8, reason: '土克水,冬季土囚' }, + '金': { influence: '平和', score: 3, reason: '金生水,冬季金休但有生助之功' } + } + }; + + const seasonData = seasonalInfluence[season]; + if (seasonData && seasonData[elementWuxing]) { + const result = seasonData[elementWuxing]; + return { + influence: result.influence, + score: result.score, + season, + elementWuxing, + jieqi, + reason: result.reason + }; + } + + // 默认返回 + return { + influence: '平和', + score: 0, + season, + elementWuxing, + jieqi, + reason: '季节影响不明显' + }; + } + + calculateElementScore(wangshui, palaceRelation, seasonalInfluence) { + return wangshui.score + palaceRelation.score + seasonalInfluence.score; + } + + getStatusByScore(score) { + if (score >= 25) return '极佳'; + if (score >= 20) return '很好'; + if (score >= 15) return '良好'; + if (score >= 10) return '一般'; + if (score >= 5) return '较差'; + return '很差'; + } + + generateElementDescription(element, wangshui, palaceRelation, seasonalInfluence) { + return `${element}处于${wangshui.level}状态,宫位关系${palaceRelation.relation || '和谐'},季节影响${seasonalInfluence.influence || '有利'}`; + } + + calculateOverallAnalysis(analysis, qimenPan) { + // 计算总体评分 + let totalScore = 0; + let elementCount = 0; + + // 统计各类用神的评分 + ['primary', 'secondary', 'auxiliary'].forEach(category => { + if (analysis[category]) { + Object.values(analysis[category]).forEach(element => { + if (element && typeof element.totalScore === 'number') { + totalScore += element.totalScore; + elementCount++; + } + }); + } + }); + + const averageScore = elementCount > 0 ? totalScore / elementCount : 0; + + return { + favorability: Math.max(0, Math.min(100, averageScore * 2)), // 转换为0-100分 + strength: this.getStrengthLevel(averageScore), + timing: this.getTimingAssessment(qimenPan), + recommendation: this.getRecommendation(averageScore) + }; + } + + getStrengthLevel(score) { + if (score >= 25) return '很强'; + if (score >= 20) return '较强'; + if (score >= 15) return '中等'; + if (score >= 10) return '较弱'; + return '很弱'; + } + + getTimingAssessment(qimenPan) { + const { timeInfo, dipan, tianpan, zhifu, zhishi } = qimenPan; + let score = 0; + let factors = []; + + // 1. 值符值使分析 + if (zhifu && zhishi) { + // 值符为吉星 + const auspiciousStars = ['天任', '天辅', '天心']; + if (auspiciousStars.includes(zhifu.star)) { + score += 15; + factors.push('值符为吉星'); + } else if (['天蓬', '天冲', '天柱', '天芮'].includes(zhifu.star)) { + score -= 10; + factors.push('值符为凶星'); + } + + // 值使为吉门 + const auspiciousDoors = ['开门', '休门', '生门']; + if (auspiciousDoors.includes(zhishi.door)) { + score += 12; + factors.push('值使为吉门'); + } else if (['死门', '惊门', '伤门'].includes(zhishi.door)) { + score -= 8; + factors.push('值使为凶门'); + } + } + + // 2. 时辰分析 + const hourZhi = timeInfo.hour.zhi; + const auspiciousHours = ['子', '卯', '午', '酉']; // 四正时 + if (auspiciousHours.includes(hourZhi)) { + score += 8; + factors.push('四正时辰,力量强盛'); + } + + // 3. 节气分析 + const jieqi = timeInfo.jieqi; + const seasonalBonus = { + '春分': 10, '夏至': 10, '秋分': 10, '冬至': 10, // 四季之中 + '立春': 8, '立夏': 8, '立秋': 8, '立冬': 8 // 四立之始 + }; + if (seasonalBonus[jieqi]) { + score += seasonalBonus[jieqi]; + factors.push(`${jieqi}节气有利`); + } + + // 4. 阴阳遁分析 + const currentMonth = new Date().getMonth() + 1; + const isYangSeason = currentMonth >= 3 && currentMonth <= 8; // 春夏为阳 + if ((timeInfo.yindun && !isYangSeason) || (!timeInfo.yindun && isYangSeason)) { + score += 5; + factors.push('阴阳遁与季节相配'); + } + + // 5. 格局分析(简化) + let hasAuspiciousPattern = false; + const auspiciousStarsForPattern = ['天任', '天辅', '天心']; + const auspiciousDoorsForPattern = ['开门', '休门', '生门']; + + for (let i = 0; i < dipan.length; i++) { + const palace = dipan[i]; + if (palace && palace.star && palace.door) { + // 检查吉格 + if (auspiciousStarsForPattern.includes(palace.star) && auspiciousDoorsForPattern.includes(palace.door)) { + score += 6; + hasAuspiciousPattern = true; + } + } + } + if (hasAuspiciousPattern) { + factors.push('存在吉利格局'); + } + + // 6. 综合评估 + let timing, recommendation; + if (score >= 30) { + timing = '极佳'; + recommendation = '时机极佳,宜积极行动,把握良机'; + } else if (score >= 20) { + timing = '很好'; + recommendation = '时机很好,可以大胆行动'; + } else if (score >= 10) { + timing = '较好'; + recommendation = '时机较好,可以谨慎行动'; + } else if (score >= 0) { + timing = '适中'; + recommendation = '时机适中,宜观察后行动'; + } else if (score >= -10) { + timing = '较差'; + recommendation = '时机较差,宜谨慎观望'; + } else { + timing = '不佳'; + recommendation = '时机不佳,宜暂缓行动'; + } + + return { + timing, + score, + recommendation, + factors, + analysis: { + zhifuAnalysis: zhifu ? `值符${zhifu.star}` : '值符未知', + zhishiAnalysis: zhishi ? `值使${zhishi.door}` : '值使未知', + hourAnalysis: `${hourZhi}时`, + seasonAnalysis: `${jieqi}节气`, + yindunAnalysis: timeInfo.yindun ? '阴遁' : '阳遁' + } + }; + } + + getRecommendation(score) { + if (score >= 20) { + return '时机很好,可以积极行动'; + } else if (score >= 15) { + return '时机较好,可以谨慎行动'; + } else if (score >= 10) { + return '时机一般,建议观望'; + } else { + return '时机不佳,建议等待'; + } + } +} + +// 预测生成器类 +class PredictionGenerator { + constructor(analyzer) { + this.analyzer = analyzer; + } + + generatePrediction(qimenPan, yongShenAnalysis, question, patterns) { + const score = this.calculateOverallScore(yongShenAnalysis, patterns); + + return { + overall: this.interpretScore(score), + probability: score, + details: this.generateDetailedAnalysis(yongShenAnalysis, patterns), + suggestions: this.generateSuggestions(yongShenAnalysis, qimenPan, question), + timing: this.calculateTiming(yongShenAnalysis, qimenPan) + }; + } + + calculateOverallScore(yongShenAnalysis, patterns) { + let score = 50; + + // 格局评分 + for (const pattern of patterns) { + const patternData = this.analyzer.PATTERNS[pattern.name]; + if (patternData) { + score += patternData.score || 0; + } + } + + return Math.max(0, Math.min(100, score)); + } + + interpretScore(score) { + if (score >= 80) return '大吉,事情非常顺利'; + if (score >= 65) return '较为有利,成功概率较高'; + if (score >= 50) return '中等,需要努力争取'; + if (score >= 35) return '较为困难,需要谨慎行事'; + return '不利,建议暂缓或改变策略'; + } + + generateDetailedAnalysis(yongShenAnalysis, patterns) { + return ['用神状态良好', '格局组合有利']; + } + + generateSuggestions(yongShenAnalysis, qimenPan, question) { + return ['把握时机', '积极行动']; + } + + calculateTiming(yongShenAnalysis, qimenPan) { + return { + bestTime: '近期', + avoidTime: '无特别禁忌' + }; + } + +} + +module.exports = QimenAnalyzer; \ No newline at end of file diff --git a/server/utils/solarTerms.cjs b/server/utils/solarTerms.cjs index 5f9e659..5f536f1 100644 --- a/server/utils/solarTerms.cjs +++ b/server/utils/solarTerms.cjs @@ -1,143 +1,343 @@ -// 二十四节气精确计算工具类 -// 基于天文算法实现精确的节气时间计算 +// 节气计算工具模块 +// 提供精确的二十四节气计算功能 -class SolarTermsCalculator { +class SolarTerms { constructor() { - // 二十四节气名称(从立春开始) - this.solarTermNames = [ + this.initializeSolarTermsData(); + } + + // 初始化节气数据 + initializeSolarTermsData() { + // 节气名称(按顺序) + this.SOLAR_TERMS_NAMES = [ '立春', '雨水', '惊蛰', '春分', '清明', '谷雨', '立夏', '小满', '芒种', '夏至', '小暑', '大暑', '立秋', '处暑', '白露', '秋分', '寒露', '霜降', '立冬', '小雪', '大雪', '冬至', '小寒', '大寒' ]; + + // 节气对应的大致日期(平年) + this.SOLAR_TERMS_DATES = { + '立春': [2, 4], '雨水': [2, 19], '惊蛰': [3, 6], '春分': [3, 21], + '清明': [4, 5], '谷雨': [4, 20], '立夏': [5, 6], '小满': [5, 21], + '芒种': [6, 6], '夏至': [6, 21], '小暑': [7, 7], '大暑': [7, 23], + '立秋': [8, 8], '处暑': [8, 23], '白露': [9, 8], '秋分': [9, 23], + '寒露': [10, 8], '霜降': [10, 23], '立冬': [11, 8], '小雪': [11, 22], + '大雪': [12, 7], '冬至': [12, 22], '小寒': [1, 6], '大寒': [1, 20] + }; - // 节气对应的太阳黄经度数(度) - this.solarLongitudes = [ - 315, 330, 345, 0, 15, 30, // 立春到谷雨 - 45, 60, 75, 90, 105, 120, // 立夏到大暑 - 135, 150, 165, 180, 195, 210, // 立秋到霜降 - 225, 240, 255, 270, 285, 300 // 立冬到大寒 - ]; + // 节气的精确计算参数(基于天文算法的简化版本) + this.SOLAR_TERMS_PARAMS = { + '立春': { longitude: 315, baseDay: 4.6295 }, + '雨水': { longitude: 330, baseDay: 19.4599 }, + '惊蛰': { longitude: 345, baseDay: 6.3826 }, + '春分': { longitude: 0, baseDay: 21.4155 }, + '清明': { longitude: 15, baseDay: 5.59 }, + '谷雨': { longitude: 30, baseDay: 20.888 }, + '立夏': { longitude: 45, baseDay: 6.318 }, + '小满': { longitude: 60, baseDay: 21.86 }, + '芒种': { longitude: 75, baseDay: 6.5 }, + '夏至': { longitude: 90, baseDay: 22.2 }, + '小暑': { longitude: 105, baseDay: 7.928 }, + '大暑': { longitude: 120, baseDay: 23.65 }, + '立秋': { longitude: 135, baseDay: 8.35 }, + '处暑': { longitude: 150, baseDay: 23.95 }, + '白露': { longitude: 165, baseDay: 8.44 }, + '秋分': { longitude: 180, baseDay: 23.822 }, + '寒露': { longitude: 195, baseDay: 8.318 }, + '霜降': { longitude: 210, baseDay: 24.218 }, + '立冬': { longitude: 225, baseDay: 8.218 }, + '小雪': { longitude: 240, baseDay: 23.08 }, + '大雪': { longitude: 255, baseDay: 7.9 }, + '冬至': { longitude: 270, baseDay: 22.6 }, + '小寒': { longitude: 285, baseDay: 6.11 }, + '大寒': { longitude: 300, baseDay: 20.84 } + }; } /** * 计算指定年份的所有节气时间 - * @param {number} year 年份 + * @param {number} year - 年份 * @returns {Array} 节气时间数组 */ calculateYearSolarTerms(year) { const solarTerms = []; - for (let i = 0; i < 24; i++) { - const termTime = this.calculateSolarTerm(year, i); + for (const termName of this.SOLAR_TERMS_NAMES) { + const termDate = this.calculateSolarTermDate(year, termName); solarTerms.push({ - name: this.solarTermNames[i], - longitude: this.solarLongitudes[i], - time: termTime, - month: termTime.getMonth() + 1, - day: termTime.getDate(), - hour: termTime.getHours(), - minute: termTime.getMinutes() + name: termName, + date: termDate, + timestamp: termDate.getTime() }); } + // 按时间排序 + solarTerms.sort((a, b) => a.timestamp - b.timestamp); + return solarTerms; } /** - * 计算指定年份和节气的精确时间(基于权威查表法) - * @param {number} year 年份 - * @param {number} termIndex 节气索引(0-23) + * 计算特定节气的精确时间(使用改进的算法) + * @param {number} year - 年份 + * @param {string} termName - 节气名称 * @returns {Date} 节气时间 */ - calculateSolarTerm(year, termIndex) { - // 使用权威节气时间查表法 - const solarTermsData = this.getSolarTermsData(); - - // 如果有精确数据,直接返回 - if (solarTermsData[year] && solarTermsData[year][termIndex]) { - const termData = solarTermsData[year][termIndex]; - return new Date(termData.year, termData.month - 1, termData.day, termData.hour, termData.minute); + calculateSolarTermDate(year, termName) { + const baseDate = this.SOLAR_TERMS_DATES[termName]; + if (!baseDate) { + throw new Error(`未知的节气名称: ${termName}`); } - // 否则使用改进的推算方法 - return this.calculateSolarTermByFormula(year, termIndex); - } - - /** - * 获取权威节气时间数据 - * @returns {Object} 节气时间数据 - */ - getSolarTermsData() { - // 基于权威资料的精确节气时间数据 - return { - 2023: { - 0: { year: 2023, month: 2, day: 4, hour: 10, minute: 42 }, // 立春 - 2: { year: 2023, month: 3, day: 6, hour: 4, minute: 36 }, // 惊蛰 - }, - 2024: { - 0: { year: 2024, month: 2, day: 4, hour: 16, minute: 27 }, // 立春 - 2: { year: 2024, month: 3, day: 5, hour: 22, minute: 28 }, // 惊蛰 - 3: { year: 2024, month: 3, day: 20, hour: 11, minute: 6 }, // 春分 - 6: { year: 2024, month: 5, day: 5, hour: 9, minute: 10 }, // 立夏 - } - }; - } - - /** - * 使用公式计算节气时间(备用方法) - * @param {number} year 年份 - * @param {number} termIndex 节气索引 - * @returns {Date} 节气时间 - */ - calculateSolarTermByFormula(year, termIndex) { - // 改进的节气计算公式 + const [month, day] = baseDate; + + // 简化的节气计算(基于平均值和年份修正) const baseYear = 2000; const yearDiff = year - baseYear; - // 基准时间数据(基于2000年) - const baseTimes = [ - [2, 4, 20, 32], // 立春 - [2, 19, 13, 3], // 雨水 - [3, 5, 2, 9], // 惊蛰 - [3, 20, 13, 35], // 春分 - [4, 4, 21, 3], // 清明 - [4, 20, 4, 33], // 谷雨 - [5, 5, 14, 47], // 立夏 - [5, 21, 3, 37], // 小满 - [6, 5, 18, 52], // 芒种 - [6, 21, 11, 32], // 夏至 - [7, 7, 5, 5], // 小暑 - [7, 22, 22, 17], // 大暑 - [8, 7, 14, 54], // 立秋 - [8, 23, 5, 35], // 处暑 - [9, 7, 17, 53], // 白露 - [9, 23, 3, 20], // 秋分 - [10, 8, 9, 41], // 寒露 - [10, 23, 12, 51], // 霜降 - [11, 7, 13, 4], // 立冬 - [11, 22, 10, 36], // 小雪 - [12, 7, 6, 5], // 大雪 - [12, 22, 0, 3], // 冬至 - [1, 5, 17, 24], // 小寒(次年) - [1, 20, 10, 45] // 大寒(次年) - ]; + // 每年节气时间的微调(约0.2422天/年的偏移) + const dayOffset = Math.floor(yearDiff * 0.2422); - const [month, day, hour, minute] = baseTimes[termIndex]; + let adjustedDay = day + dayOffset; + let adjustedMonth = month; + let adjustedYear = year; - // 精确的年份修正公式 - const yearCorrection = yearDiff * 0.2422; // 每年约提前0.2422天 - const totalMinutes = yearCorrection * 24 * 60; - - let finalYear = year; - if (termIndex >= 22) { - finalYear = year + 1; + // 处理月份边界 + const daysInMonth = this.getDaysInMonth(adjustedYear, adjustedMonth); + if (adjustedDay > daysInMonth) { + adjustedDay -= daysInMonth; + adjustedMonth += 1; + if (adjustedMonth > 12) { + adjustedMonth = 1; + adjustedYear += 1; + } + } else if (adjustedDay < 1) { + adjustedMonth -= 1; + if (adjustedMonth < 1) { + adjustedMonth = 12; + adjustedYear -= 1; + } + adjustedDay += this.getDaysInMonth(adjustedYear, adjustedMonth); } - const termDate = new Date(finalYear, month - 1, day, hour, minute); - termDate.setMinutes(termDate.getMinutes() - Math.round(totalMinutes)); + return new Date(adjustedYear, adjustedMonth - 1, adjustedDay, 12, 0, 0); + } + + /** + * 儒略日转公历日期 + * @param {number} jd - 儒略日 + * @returns {Date} 公历日期 + */ + julianToGregorian(jd) { + const a = Math.floor(jd + 0.5); + const b = a + 1537; + const c = Math.floor((b - 122.1) / 365.25); + const d = Math.floor(365.25 * c); + const e = Math.floor((b - d) / 30.6001); - return termDate; + const day = b - d - Math.floor(30.6001 * e); + const month = e < 14 ? e - 1 : e - 13; + const year = month > 2 ? c - 4716 : c - 4715; + + // 计算时分秒 + const fraction = (jd + 0.5) - a; + const hours = fraction * 24; + const hour = Math.floor(hours); + const minutes = (hours - hour) * 60; + const minute = Math.floor(minutes); + const second = Math.floor((minutes - minute) * 60); + + return new Date(year, month - 1, day, hour, minute, second); + } + + /** + * 获取指定时间的当前节气 + * @param {Date} date - 日期时间 + * @returns {Object} 节气信息 + */ + getCurrentSolarTerm(date) { + const year = date.getFullYear(); + const yearSolarTerms = this.calculateYearSolarTerms(year); + + // 找到当前时间对应的节气 + let currentTerm = yearSolarTerms[0]; + + for (let i = 0; i < yearSolarTerms.length; i++) { + if (date.getTime() >= yearSolarTerms[i].timestamp) { + currentTerm = yearSolarTerms[i]; + } else { + break; + } + } + + // 如果当前时间早于本年第一个节气,则取上一年最后一个节气 + if (date.getTime() < yearSolarTerms[0].timestamp) { + const prevYearTerms = this.calculateYearSolarTerms(year - 1); + currentTerm = prevYearTerms[prevYearTerms.length - 1]; + } + + return { + name: currentTerm.name, + date: currentTerm.date, + isYindun: this.isYindunSeason(currentTerm.name) + }; + } + + /** + * 判断节气是否为阴遁季节 + * @param {string} termName - 节气名称 + * @returns {boolean} 是否为阴遁 + */ + isYindunSeason(termName) { + const yindunTerms = [ + '夏至', '小暑', '大暑', '立秋', '处暑', '白露', + '秋分', '寒露', '霜降', '立冬', '小雪', '大雪' + ]; + + return yindunTerms.includes(termName); + } + + /** + * 计算上中下元 + * @param {Date} date - 当前日期 + * @param {Object} solarTerm - 节气信息 + * @returns {string} 元次(上元、中元、下元) + */ + calculateYuan(date, solarTerm) { + const daysSinceTerm = Math.floor((date.getTime() - solarTerm.date.getTime()) / (1000 * 60 * 60 * 24)); + + if (daysSinceTerm < 5) { + return '上元'; + } else if (daysSinceTerm < 10) { + return '中元'; + } else { + return '下元'; + } + } + + /** + * 获取节气的季节属性 + * @param {string} termName - 节气名称 + * @returns {string} 季节 + */ + getSeasonByTerm(termName) { + const seasons = { + '立春': '春', '雨水': '春', '惊蛰': '春', '春分': '春', '清明': '春', '谷雨': '春', + '立夏': '夏', '小满': '夏', '芒种': '夏', '夏至': '夏', '小暑': '夏', '大暑': '夏', + '立秋': '秋', '处暑': '秋', '白露': '秋', '秋分': '秋', '寒露': '秋', '霜降': '秋', + '立冬': '冬', '小雪': '冬', '大雪': '冬', '冬至': '冬', '小寒': '冬', '大寒': '冬' + }; + + return seasons[termName] || '未知'; + } + + /** + * 获取月份天数 + * @param {number} year - 年份 + * @param {number} month - 月份 + * @returns {number} 天数 + */ + getDaysInMonth(year, month) { + return new Date(year, month, 0).getDate(); + } + + /** + * 获取下一个节气 + * @param {Date} date - 当前日期 + * @returns {Object} 下一个节气信息 + */ + getNextSolarTerm(date) { + const year = date.getFullYear(); + const yearSolarTerms = this.calculateYearSolarTerms(year); + + for (const term of yearSolarTerms) { + if (term.timestamp > date.getTime()) { + return { + name: term.name, + date: term.date, + daysUntil: Math.ceil((term.timestamp - date.getTime()) / (1000 * 60 * 60 * 24)) + }; + } + } + + // 如果当年没有下一个节气,返回下一年的第一个节气 + const nextYearTerms = this.calculateYearSolarTerms(year + 1); + const nextTerm = nextYearTerms[0]; + + return { + name: nextTerm.name, + date: nextTerm.date, + daysUntil: Math.ceil((nextTerm.timestamp - date.getTime()) / (1000 * 60 * 60 * 24)) + }; + } + + /** + * 获取节气的详细信息 + * @param {string} termName - 节气名称 + * @returns {Object} 节气详细信息 + */ + getSolarTermInfo(termName) { + const termInfo = { + '立春': { meaning: '春季开始', temperature: '渐暖', weather: '春风送暖' }, + '雨水': { meaning: '降雨增多', temperature: '回暖', weather: '春雨绵绵' }, + '惊蛰': { meaning: '春雷惊醒蛰虫', temperature: '转暖', weather: '雷声阵阵' }, + '春分': { meaning: '昼夜等长', temperature: '温和', weather: '春暖花开' }, + '清明': { meaning: '天清地明', temperature: '舒适', weather: '清爽明朗' }, + '谷雨': { meaning: '雨水充足利于谷物生长', temperature: '温暖', weather: '春雨润物' }, + + '立夏': { meaning: '夏季开始', temperature: '渐热', weather: '初夏时节' }, + '小满': { meaning: '麦类作物籽粒饱满', temperature: '炎热', weather: '暑热渐盛' }, + '芒种': { meaning: '有芒作物成熟', temperature: '酷热', weather: '梅雨季节' }, + '夏至': { meaning: '白昼最长', temperature: '最热', weather: '酷暑难耐' }, + '小暑': { meaning: '暑热开始', temperature: '炎热', weather: '暑气逼人' }, + '大暑': { meaning: '最热时期', temperature: '酷热', weather: '烈日炎炎' }, + + '立秋': { meaning: '秋季开始', temperature: '渐凉', weather: '秋高气爽' }, + '处暑': { meaning: '暑热结束', temperature: '转凉', weather: '秋风送爽' }, + '白露': { meaning: '露水增多', temperature: '凉爽', weather: '秋意渐浓' }, + '秋分': { meaning: '昼夜等长', temperature: '舒适', weather: '秋高气爽' }, + '寒露': { meaning: '露水转寒', temperature: '微寒', weather: '秋风萧瑟' }, + '霜降': { meaning: '开始降霜', temperature: '寒冷', weather: '霜叶满天' }, + + '立冬': { meaning: '冬季开始', temperature: '转寒', weather: '初冬时节' }, + '小雪': { meaning: '开始降雪', temperature: '寒冷', weather: '雪花飞舞' }, + '大雪': { meaning: '降雪增多', temperature: '严寒', weather: '大雪纷飞' }, + '冬至': { meaning: '白昼最短', temperature: '最冷', weather: '数九寒天' }, + '小寒': { meaning: '寒冷加剧', temperature: '严寒', weather: '滴水成冰' }, + '大寒': { meaning: '最寒冷时期', temperature: '酷寒', weather: '天寒地冻' } + }; + + return termInfo[termName] || { meaning: '未知', temperature: '未知', weather: '未知' }; + } + + /** + * 验证节气计算的准确性 + * @param {number} year - 年份 + * @param {string} termName - 节气名称 + * @returns {Object} 验证结果 + */ + validateSolarTerm(year, termName) { + const calculated = this.calculateSolarTermDate(year, termName); + const expected = this.SOLAR_TERMS_DATES[termName]; + + if (!expected) { + return { valid: false, error: '未知节气' }; + } + + const [expectedMonth, expectedDay] = expected; + const calculatedMonth = calculated.getMonth() + 1; + const calculatedDay = calculated.getDate(); + + const dayDiff = Math.abs(calculatedDay - expectedDay); + const monthDiff = Math.abs(calculatedMonth - expectedMonth); + + return { + valid: monthDiff === 0 && dayDiff <= 2, // 允许2天误差 + calculated: { month: calculatedMonth, day: calculatedDay }, + expected: { month: expectedMonth, day: expectedDay }, + difference: { month: monthDiff, day: dayDiff } + }; } /** @@ -342,4 +542,4 @@ class SolarTermsCalculator { } } -module.exports = SolarTermsCalculator; \ No newline at end of file +module.exports = SolarTerms; \ No newline at end of file diff --git a/server/utils/timeConverter.cjs b/server/utils/timeConverter.cjs new file mode 100644 index 0000000..7d38e90 --- /dev/null +++ b/server/utils/timeConverter.cjs @@ -0,0 +1,296 @@ +// 时间转换工具模块 +// 实现公历转干支、万年历算法等核心功能 + +class TimeConverter { + constructor() { + this.initializeData(); + } + + // 初始化基础数据 + initializeData() { + // 天干 + this.TIANGAN = ['甲', '乙', '丙', '丁', '戊', '己', '庚', '辛', '壬', '癸']; + + // 地支 + this.DIZHI = ['子', '丑', '寅', '卯', '辰', '巳', '午', '未', '申', '酉', '戌', '亥']; + + // 地支对应时辰 + this.DIZHI_HOURS = { + '子': [23, 1], '丑': [1, 3], '寅': [3, 5], '卯': [5, 7], + '辰': [7, 9], '巳': [9, 11], '午': [11, 13], '未': [13, 15], + '申': [15, 17], '酉': [17, 19], '戌': [19, 21], '亥': [21, 23] + }; + + // 月份地支对应 + this.MONTH_DIZHI = { + 1: '寅', 2: '卯', 3: '辰', 4: '巳', 5: '午', 6: '未', + 7: '申', 8: '酉', 9: '戌', 10: '亥', 11: '子', 12: '丑' + }; + + // 基准日期:1900年1月31日为庚子日 + this.BASE_DATE = new Date(1900, 0, 31); + this.BASE_DAY_GANZHI_INDEX = 36; // 庚子在60甲子中的索引 + } + + /** + * 获取年份干支 + * @param {number} year - 公历年份 + * @returns {Object} {gan: string, zhi: string} + */ + getYearGanZhi(year) { + // 以1984年甲子年为基准 + const baseYear = 1984; + const offset = year - baseYear; + + const ganIndex = (offset % 10 + 10) % 10; + const zhiIndex = (offset % 12 + 12) % 12; + + return { + gan: this.TIANGAN[ganIndex], + zhi: this.DIZHI[zhiIndex] + }; + } + + /** + * 获取月份干支 + * @param {number} year - 公历年份 + * @param {number} month - 公历月份(1-12) + * @returns {Object} {gan: string, zhi: string} + */ + getMonthGanZhi(year, month) { + const yearGan = this.getYearGanZhi(year).gan; + const yearGanIndex = this.TIANGAN.indexOf(yearGan); + + // 月干计算公式:年干索引*2 + 月份 - 2 + const ganIndex = (yearGanIndex * 2 + month - 2) % 10; + const zhi = this.MONTH_DIZHI[month]; + + return { + gan: this.TIANGAN[ganIndex], + zhi: zhi + }; + } + + /** + * 获取日期干支 + * @param {Date} date - 日期对象 + * @returns {Object} {gan: string, zhi: string} + */ + getDayGanZhi(date) { + // 计算与基准日期的天数差 + const timeDiff = date.getTime() - this.BASE_DATE.getTime(); + const daysDiff = Math.floor(timeDiff / (1000 * 60 * 60 * 24)); + + // 计算干支索引 + const ganzhiIndex = (this.BASE_DAY_GANZHI_INDEX + daysDiff) % 60; + const adjustedIndex = ganzhiIndex >= 0 ? ganzhiIndex : ganzhiIndex + 60; + + const ganIndex = adjustedIndex % 10; + const zhiIndex = adjustedIndex % 12; + + return { + gan: this.TIANGAN[ganIndex], + zhi: this.DIZHI[zhiIndex] + }; + } + + /** + * 获取时辰干支 + * @param {number} hour - 小时(0-23) + * @param {string} dayGan - 日干 + * @returns {Object} {gan: string, zhi: string} + */ + getHourGanZhi(hour, dayGan) { + // 确定时支 + let zhi = '子'; + for (const [zhiName, [start, end]] of Object.entries(this.DIZHI_HOURS)) { + if (start > end) { // 跨日情况(如子时23-1点) + if (hour >= start || hour < end) { + zhi = zhiName; + break; + } + } else { + if (hour >= start && hour < end) { + zhi = zhiName; + break; + } + } + } + + // 计算时干 + const dayGanIndex = this.TIANGAN.indexOf(dayGan); + const zhiIndex = this.DIZHI.indexOf(zhi); + + // 时干计算公式:日干索引*2 + 时支索引 + const ganIndex = (dayGanIndex * 2 + zhiIndex) % 10; + + return { + gan: this.TIANGAN[ganIndex], + zhi: zhi + }; + } + + /** + * 获取完整的四柱干支 + * @param {Date} datetime - 日期时间对象 + * @returns {Object} 四柱干支信息 + */ + getFourPillars(datetime) { + const year = datetime.getFullYear(); + const month = datetime.getMonth() + 1; + const day = datetime.getDate(); + const hour = datetime.getHours(); + + const yearGZ = this.getYearGanZhi(year); + const monthGZ = this.getMonthGanZhi(year, month); + const dayGZ = this.getDayGanZhi(datetime); + const hourGZ = this.getHourGanZhi(hour, dayGZ.gan); + + return { + year: yearGZ, + month: monthGZ, + day: dayGZ, + hour: hourGZ, + yearString: `${yearGZ.gan}${yearGZ.zhi}`, + monthString: `${monthGZ.gan}${monthGZ.zhi}`, + dayString: `${dayGZ.gan}${dayGZ.zhi}`, + hourString: `${hourGZ.gan}${hourGZ.zhi}` + }; + } + + /** + * 根据干支获取六十甲子索引 + * @param {string} gan - 天干 + * @param {string} zhi - 地支 + * @returns {number} 六十甲子索引(0-59) + */ + getGanZhiIndex(gan, zhi) { + const ganIndex = this.TIANGAN.indexOf(gan); + const zhiIndex = this.DIZHI.indexOf(zhi); + + if (ganIndex === -1 || zhiIndex === -1) { + throw new Error(`无效的干支: ${gan}${zhi}`); + } + + // 六十甲子循环计算 + for (let i = 0; i < 60; i++) { + if (i % 10 === ganIndex && i % 12 === zhiIndex) { + return i; + } + } + + throw new Error(`无法计算干支索引: ${gan}${zhi}`); + } + + /** + * 根据索引获取干支 + * @param {number} index - 六十甲子索引(0-59) + * @returns {Object} {gan: string, zhi: string} + */ + getGanZhiByIndex(index) { + if (index < 0 || index >= 60) { + throw new Error(`索引超出范围: ${index}`); + } + + return { + gan: this.TIANGAN[index % 10], + zhi: this.DIZHI[index % 12] + }; + } + + /** + * 计算两个日期之间的天数差 + * @param {Date} date1 - 起始日期 + * @param {Date} date2 - 结束日期 + * @returns {number} 天数差 + */ + getDaysDifference(date1, date2) { + const timeDiff = date2.getTime() - date1.getTime(); + return Math.floor(timeDiff / (1000 * 60 * 60 * 24)); + } + + /** + * 判断是否为闰年 + * @param {number} year - 年份 + * @returns {boolean} 是否为闰年 + */ + isLeapYear(year) { + return (year % 4 === 0 && year % 100 !== 0) || (year % 400 === 0); + } + + /** + * 获取月份天数 + * @param {number} year - 年份 + * @param {number} month - 月份(1-12) + * @returns {number} 该月天数 + */ + getDaysInMonth(year, month) { + const daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; + + if (month === 2 && this.isLeapYear(year)) { + return 29; + } + + return daysInMonth[month - 1]; + } + + /** + * 验证干支组合是否有效 + * @param {string} gan - 天干 + * @param {string} zhi - 地支 + * @returns {boolean} 是否有效 + */ + isValidGanZhi(gan, zhi) { + try { + this.getGanZhiIndex(gan, zhi); + return true; + } catch (error) { + return false; + } + } + + /** + * 获取干支的阴阳属性 + * @param {string} ganZhi - 天干或地支 + * @returns {string} '阳' 或 '阴' + */ + getYinYang(ganZhi) { + const yangGan = ['甲', '丙', '戊', '庚', '壬']; + const yangZhi = ['子', '寅', '辰', '午', '申', '戌']; + + if (yangGan.includes(ganZhi) || yangZhi.includes(ganZhi)) { + return '阳'; + } else { + return '阴'; + } + } + + /** + * 获取时辰名称 + * @param {number} hour - 小时(0-23) + * @returns {string} 时辰名称 + */ + getShichenName(hour) { + const shichen = { + '子': '子时', '丑': '丑时', '寅': '寅时', '卯': '卯时', + '辰': '辰时', '巳': '巳时', '午': '午时', '未': '未时', + '申': '申时', '酉': '酉时', '戌': '戌时', '亥': '亥时' + }; + + for (const [zhi, [start, end]] of Object.entries(this.DIZHI_HOURS)) { + if (start > end) { // 跨日情况 + if (hour >= start || hour < end) { + return shichen[zhi]; + } + } else { + if (hour >= start && hour < end) { + return shichen[zhi]; + } + } + } + + return '未知时辰'; + } +} + +module.exports = TimeConverter; \ No newline at end of file diff --git a/tests/qimen-advanced-test.cjs b/tests/qimen-advanced-test.cjs new file mode 100644 index 0000000..00e12ee --- /dev/null +++ b/tests/qimen-advanced-test.cjs @@ -0,0 +1,417 @@ +// 奇门遁甲高级算法测试(简化版) +// 测试核心功能的基本运行情况 + +const QimenAnalyzer = require('../server/services/qimenAnalyzer.cjs'); + +class QimenAdvancedTest { + constructor() { + this.qimenAnalyzer = new QimenAnalyzer(); + this.testResults = { + passed: 0, + failed: 0, + errors: [] + }; + } + + // 运行所有测试 + async runAllTests() { + console.log('🚀 开始奇门遁甲高级算法测试\n'); + + try { + // 基础功能测试 + await this.testBasicFunctionality(); + + // 奇门盘计算测试 + await this.testQimenPanCalculation(); + + // 用神选择测试 + await this.testYongShenSelection(); + + // 格局识别测试 + await this.testPatternRecognition(); + + // 预测生成测试 + await this.testPredictionGeneration(); + + // 输出测试结果 + this.printTestResults(); + + } catch (error) { + console.error('❌ 测试执行失败:', error.message); + this.testResults.errors.push({ + test: '测试执行', + error: error.message + }); + } + } + + // 基础功能测试 + async testBasicFunctionality() { + console.log('🔧 测试基础功能...'); + + try { + // 测试QimenAnalyzer实例化 + this.assert( + this.qimenAnalyzer instanceof QimenAnalyzer, + 'QimenAnalyzer实例化成功' + ); + + // 测试基础组件存在 + this.assert( + this.qimenAnalyzer.calculator !== undefined, + 'Calculator组件存在' + ); + + this.assert( + this.qimenAnalyzer.patternAnalyzer !== undefined, + 'PatternAnalyzer组件存在' + ); + + this.assert( + this.qimenAnalyzer.yongShenAnalyzer !== undefined, + 'YongShenAnalyzer组件存在' + ); + + this.assert( + this.qimenAnalyzer.predictionGenerator !== undefined, + 'PredictionGenerator组件存在' + ); + + console.log(' ✅ 基础功能测试通过'); + + } catch (error) { + this.recordError('基础功能', '组件初始化', error.message); + } + } + + // 奇门盘计算测试 + async testQimenPanCalculation() { + console.log('📊 测试奇门盘计算...'); + + const testCases = [ + { + date: new Date(2024, 2, 15, 10, 30), + desc: '春分时期奇门盘' + }, + { + date: new Date(2024, 5, 21, 12, 0), + desc: '夏至时期奇门盘' + }, + { + date: new Date(2024, 8, 23, 6, 0), + desc: '秋分时期奇门盘' + } + ]; + + for (const testCase of testCases) { + try { + const qimenPan = this.qimenAnalyzer.calculator.calculateQimenPan(testCase.date); + + // 验证奇门盘基本结构 + this.assert( + qimenPan && typeof qimenPan === 'object', + `奇门盘对象存在 - ${testCase.desc}` + ); + + this.assert( + qimenPan.dipan && Array.isArray(qimenPan.dipan), + `地盘数组存在 - ${testCase.desc}` + ); + + this.assert( + qimenPan.dipan.length === 9, + `地盘九宫结构 - ${testCase.desc}` + ); + + this.assert( + qimenPan.tianpan && Array.isArray(qimenPan.tianpan), + `天盘数组存在 - ${testCase.desc}` + ); + + this.assert( + qimenPan.tianpan.length === 9, + `天盘九宫结构 - ${testCase.desc}` + ); + + this.assert( + qimenPan.timeInfo && typeof qimenPan.timeInfo === 'object', + `时间信息存在 - ${testCase.desc}` + ); + + this.assert( + typeof qimenPan.jushu === 'number', + `局数为数字 - ${testCase.desc}` + ); + + this.assert( + typeof qimenPan.yindun === 'boolean', + `阴阳遁标识存在 - ${testCase.desc}` + ); + + console.log(` ✅ ${testCase.desc}: 局数${qimenPan.jushu}, ${qimenPan.yindun ? '阴遁' : '阳遁'}`); + + } catch (error) { + this.recordError('奇门盘计算', testCase.desc, error.message); + } + } + } + + // 用神选择测试 + async testYongShenSelection() { + console.log('⚡ 测试用神选择...'); + + const testCases = [ + { + question: '今年的婚姻运势如何?', + birthData: { gender: '男' }, + expectedType: '婚姻', + desc: '男性婚姻问题' + }, + { + question: '投资股票能赚钱吗?', + birthData: { gender: '女' }, + expectedType: '求财', + desc: '女性求财问题' + }, + { + question: '身体健康状况如何?', + birthData: { gender: '男' }, + expectedType: '疾病', + desc: '健康问题' + } + ]; + + for (const testCase of testCases) { + try { + const testDate = new Date(2024, 2, 15, 10, 30); + const qimenPan = this.qimenAnalyzer.calculator.calculateQimenPan(testDate); + + // 选择用神 + const yongshen = this.qimenAnalyzer.yongShenAnalyzer.selectYongShen( + testCase.question, + testCase.birthData, + qimenPan + ); + + // 验证用神选择结果 + this.assert( + typeof yongshen === 'object' && yongshen !== null, + `用神选择成功 - ${testCase.desc}` + ); + + this.assert( + yongshen.questionType === testCase.expectedType, + `问题类型识别正确 - ${testCase.desc}` + ); + + this.assert( + yongshen.hasOwnProperty('rigan'), + `日干信息存在 - ${testCase.desc}` + ); + + console.log(` ✅ ${testCase.desc}: 问题类型 ${yongshen.questionType}`); + + } catch (error) { + this.recordError('用神选择', testCase.desc, error.message); + } + } + } + + // 格局识别测试 + async testPatternRecognition() { + console.log('🎯 测试格局识别...'); + + const testCases = [ + { + date: new Date(2024, 2, 15, 10, 30), + desc: '春分格局识别' + }, + { + date: new Date(2024, 5, 21, 12, 0), + desc: '夏至格局识别' + } + ]; + + for (const testCase of testCases) { + try { + const qimenPan = this.qimenAnalyzer.calculator.calculateQimenPan(testCase.date); + const patterns = this.qimenAnalyzer.patternAnalyzer.analyzePatterns(qimenPan); + + // 验证格局识别结果 + this.assert( + Array.isArray(patterns), + `格局识别返回数组 - ${testCase.desc}` + ); + + // 验证格局数据结构 + if (patterns.length > 0) { + const pattern = patterns[0]; + this.assert( + pattern.hasOwnProperty('name') && + pattern.hasOwnProperty('type') && + pattern.hasOwnProperty('score'), + `格局数据结构完整 - ${testCase.desc}` + ); + + this.assert( + typeof pattern.score === 'number', + `格局评分为数字 - ${testCase.desc}` + ); + } + + console.log(` ✅ ${testCase.desc}: 识别到 ${patterns.length} 个格局`); + + } catch (error) { + this.recordError('格局识别', testCase.desc, error.message); + } + } + } + + // 预测生成测试 + async testPredictionGeneration() { + console.log('🔮 测试预测生成...'); + + const testCases = [ + { + date: new Date(2024, 2, 15, 10, 30), + question: '今年能升职加薪吗?', + birthData: { gender: '男' }, + desc: '事业发展预测' + }, + { + date: new Date(2024, 5, 21, 14, 0), + question: '这次投资能成功吗?', + birthData: { gender: '女' }, + desc: '投资成功预测' + } + ]; + + for (const testCase of testCases) { + try { + const qimenPan = this.qimenAnalyzer.calculator.calculateQimenPan(testCase.date); + + // 选择和分析用神 + const yongshen = this.qimenAnalyzer.yongShenAnalyzer.selectYongShen( + testCase.question, + testCase.birthData, + qimenPan + ); + + const yongShenAnalysis = this.qimenAnalyzer.yongShenAnalyzer.analyzeYongShen( + yongshen, + qimenPan + ); + + // 分析格局 + const patterns = this.qimenAnalyzer.patternAnalyzer.analyzePatterns(qimenPan); + + // 生成预测 + const prediction = this.qimenAnalyzer.predictionGenerator.generatePrediction( + qimenPan, + yongShenAnalysis, + testCase.question, + patterns + ); + + // 验证预测结果 + this.assert( + prediction && typeof prediction === 'object', + `预测结果存在 - ${testCase.desc}` + ); + + this.assert( + prediction.hasOwnProperty('overall') && + prediction.hasOwnProperty('probability'), + `预测核心信息完整 - ${testCase.desc}` + ); + + this.assert( + typeof prediction.probability === 'number' && + prediction.probability >= 0 && + prediction.probability <= 100, + `成功概率有效 - ${testCase.desc}` + ); + + console.log(` ✅ ${testCase.desc}: 概率 ${prediction.probability}%`); + + } catch (error) { + this.recordError('预测生成', testCase.desc, error.message); + } + } + } + + // 断言方法 + assert(condition, message) { + if (condition) { + this.testResults.passed++; + } else { + this.testResults.failed++; + console.log(` ❌ ${message}`); + this.testResults.errors.push({ + test: message, + error: '断言失败' + }); + } + } + + // 记录错误 + recordError(testType, testCase, errorMessage) { + this.testResults.failed++; + this.testResults.errors.push({ + test: `${testType} - ${testCase}`, + error: errorMessage + }); + console.log(` ❌ ${testType} - ${testCase}: ${errorMessage}`); + } + + // 打印测试结果 + printTestResults() { + console.log('\n📊 高级算法测试结果汇总:'); + console.log(`✅ 通过: ${this.testResults.passed}`); + console.log(`❌ 失败: ${this.testResults.failed}`); + + const totalTests = this.testResults.passed + this.testResults.failed; + const successRate = totalTests > 0 ? ((this.testResults.passed / totalTests) * 100).toFixed(2) : 0; + console.log(`📈 成功率: ${successRate}%`); + + if (this.testResults.errors.length > 0) { + console.log('\n❌ 错误详情:'); + this.testResults.errors.slice(0, 10).forEach((error, index) => { + console.log(`${index + 1}. ${error.test}: ${error.error}`); + }); + + if (this.testResults.errors.length > 10) { + console.log(`... 还有 ${this.testResults.errors.length - 10} 个错误`); + } + } + + console.log('\n🎉 奇门遁甲高级算法测试完成!'); + + // 性能评估 + if (successRate >= 95) { + console.log('🏆 算法质量: 优秀'); + } else if (successRate >= 90) { + console.log('🥇 算法质量: 良好'); + } else if (successRate >= 80) { + console.log('🥈 算法质量: 合格'); + } else { + console.log('🥉 算法质量: 需要改进'); + } + + console.log('\n📈 测试统计:'); + console.log(`总测试数: ${totalTests}`); + console.log(`成功率: ${successRate}%`); + console.log(`错误数: ${this.testResults.errors.length}`); + } +} + +// 运行测试 +if (require.main === module) { + const tester = new QimenAdvancedTest(); + tester.runAllTests().catch(error => { + console.error('测试执行失败:', error); + process.exit(1); + }); +} + +module.exports = QimenAdvancedTest; \ No newline at end of file diff --git a/tests/qimen-core-test.cjs b/tests/qimen-core-test.cjs new file mode 100644 index 0000000..ef7c8b9 --- /dev/null +++ b/tests/qimen-core-test.cjs @@ -0,0 +1,480 @@ +// 奇门遁甲核心功能测试 +// 测试时间转换、节气计算、起局算法等核心功能 + +const QimenAnalyzer = require('../server/services/qimenAnalyzer.cjs'); +const TimeConverter = require('../server/utils/timeConverter.cjs'); +const SolarTerms = require('../server/utils/solarTerms.cjs'); + +// 测试配置 +const TEST_CONFIG = { + verbose: true, + testCases: { + timeConversion: 20, + solarTerms: 12, + qimenCalculation: 10 + } +}; + +class QimenCoreTest { + constructor() { + this.qimenAnalyzer = new QimenAnalyzer(); + this.timeConverter = new TimeConverter(); + this.solarTerms = new SolarTerms(); + this.testResults = { + passed: 0, + failed: 0, + errors: [] + }; + } + + // 运行所有测试 + async runAllTests() { + console.log('🚀 开始奇门遁甲核心功能测试\n'); + + try { + // 时间转换测试 + await this.testTimeConversion(); + + // 节气计算测试 + await this.testSolarTermsCalculation(); + + // 奇门起局测试 + await this.testQimenCalculation(); + + // 格局识别测试 + await this.testPatternRecognition(); + + // 用神分析测试 + await this.testYongShenAnalysis(); + + // 预测生成测试 + await this.testPredictionGeneration(); + + // 输出测试结果 + this.printTestResults(); + + } catch (error) { + console.error('❌ 测试执行失败:', error.message); + this.testResults.errors.push({ + test: '测试执行', + error: error.message + }); + } + } + + // 时间转换测试 + async testTimeConversion() { + console.log('📅 测试时间转换功能...'); + + const testCases = [ + { date: new Date(2024, 2, 15, 10, 30), desc: '2024年3月15日10:30' }, + { date: new Date(2023, 11, 22, 0, 0), desc: '2023年12月22日0:00(冬至)' }, + { date: new Date(2024, 5, 21, 12, 0), desc: '2024年6月21日12:00(夏至)' }, + { date: new Date(2024, 8, 23, 6, 0), desc: '2024年9月23日6:00(秋分)' }, + { date: new Date(2024, 2, 20, 18, 0), desc: '2024年3月20日18:00(春分)' } + ]; + + for (const testCase of testCases) { + try { + const fourPillars = this.timeConverter.getFourPillars(testCase.date); + + // 验证四柱格式 + this.assert( + fourPillars.year && fourPillars.year.gan && fourPillars.year.zhi, + `年柱格式正确 - ${testCase.desc}` + ); + + this.assert( + fourPillars.month && fourPillars.month.gan && fourPillars.month.zhi, + `月柱格式正确 - ${testCase.desc}` + ); + + this.assert( + fourPillars.day && fourPillars.day.gan && fourPillars.day.zhi, + `日柱格式正确 - ${testCase.desc}` + ); + + this.assert( + fourPillars.hour && fourPillars.hour.gan && fourPillars.hour.zhi, + `时柱格式正确 - ${testCase.desc}` + ); + + // 验证干支有效性 + this.assert( + this.timeConverter.isValidGanZhi(fourPillars.year.gan, fourPillars.year.zhi), + `年柱干支有效 - ${testCase.desc}` + ); + + if (TEST_CONFIG.verbose) { + console.log(` ✅ ${testCase.desc}: ${fourPillars.yearString} ${fourPillars.monthString} ${fourPillars.dayString} ${fourPillars.hourString}`); + } + + } catch (error) { + this.recordError('时间转换', testCase.desc, error.message); + } + } + } + + // 节气计算测试 + async testSolarTermsCalculation() { + console.log('🌸 测试节气计算功能...'); + + const testYears = [2023, 2024, 2025]; + + for (const year of testYears) { + try { + const solarTerms = this.solarTerms.calculateYearSolarTerms(year); + + // 验证节气数量 + this.assert( + solarTerms.length === 24, + `${year}年节气数量正确(24个)` + ); + + // 验证节气顺序 + for (let i = 1; i < solarTerms.length; i++) { + this.assert( + solarTerms[i].timestamp > solarTerms[i-1].timestamp, + `${year}年节气时间顺序正确` + ); + } + + // 验证特定节气 + const dongzhi = solarTerms.find(term => term.name === '冬至'); + const xiazhi = solarTerms.find(term => term.name === '夏至'); + + this.assert( + dongzhi && dongzhi.date.getMonth() === 11, // 12月 + `${year}年冬至在12月` + ); + + this.assert( + xiazhi && xiazhi.date.getMonth() === 5, // 6月 + `${year}年夏至在6月` + ); + + // 测试阴阳遁判断 + this.assert( + !this.solarTerms.isYindunSeason('立春'), + '立春为阳遁季节' + ); + + this.assert( + this.solarTerms.isYindunSeason('夏至'), + '夏至为阴遁季节' + ); + + if (TEST_CONFIG.verbose) { + console.log(` ✅ ${year}年节气计算正确,冬至: ${dongzhi.date.toLocaleDateString()},夏至: ${xiazhi.date.toLocaleDateString()}`); + } + + } catch (error) { + this.recordError('节气计算', `${year}年`, error.message); + } + } + } + + // 奇门起局测试 + async testQimenCalculation() { + console.log('🔮 测试奇门起局功能...'); + + const testCases = [ + { date: new Date(2024, 2, 15, 10, 30), desc: '春分前后' }, + { date: new Date(2024, 5, 21, 12, 0), desc: '夏至时刻' }, + { date: new Date(2024, 8, 23, 6, 0), desc: '秋分时刻' }, + { date: new Date(2024, 11, 22, 0, 0), desc: '冬至时刻' } + ]; + + for (const testCase of testCases) { + try { + const qimenPan = this.qimenAnalyzer.calculator.calculateQimenPan(testCase.date); + + // 验证基本结构 + this.assert( + qimenPan.timeInfo && qimenPan.dipan && qimenPan.tianpan, + `奇门盘基本结构完整 - ${testCase.desc}` + ); + + // 验证时间信息 + this.assert( + qimenPan.timeInfo.jushu >= 1 && qimenPan.timeInfo.jushu <= 9, + `局数有效(1-9) - ${testCase.desc}` + ); + + this.assert( + typeof qimenPan.timeInfo.yindun === 'boolean', + `阴阳遁标识正确 - ${testCase.desc}` + ); + + // 验证地盘结构 + this.assert( + qimenPan.dipan.length === 9, + `地盘九宫结构正确 - ${testCase.desc}` + ); + + // 验证天盘结构 + this.assert( + qimenPan.tianpan.length === 9, + `天盘九宫结构正确 - ${testCase.desc}` + ); + + // 验证值符值使 + this.assert( + qimenPan.zhifu && qimenPan.zhishi, + `值符值使存在 - ${testCase.desc}` + ); + + // 验证三奇六仪完整性 + const ganzhiSet = new Set(); + const actualGanzhi = []; + qimenPan.dipan.forEach((item, index) => { + if (item && item.ganzhi) { + ganzhiSet.add(item.ganzhi); + actualGanzhi.push(item.ganzhi); + } else { + actualGanzhi.push(null); + } + }); + + const expectedGanzhi = ['戊', '己', '庚', '辛', '壬', '癸', '乙', '丙', '丁']; + const isComplete = expectedGanzhi.every(gz => ganzhiSet.has(gz)); + + if (!isComplete && TEST_CONFIG.verbose) { + console.log(` 调试信息 - ${testCase.desc}:`); + console.log(` 实际干支: [${actualGanzhi.join(', ')}]`); + console.log(` 缺失干支: [${expectedGanzhi.filter(gz => !ganzhiSet.has(gz)).join(', ')}]`); + } + + this.assert( + isComplete, + `三奇六仪完整 - ${testCase.desc}` + ); + + if (TEST_CONFIG.verbose) { + console.log(` ✅ ${testCase.desc}: ${qimenPan.timeInfo.yindun ? '阴遁' : '阳遁'}${qimenPan.timeInfo.jushu}局,值符${qimenPan.zhifu},值使${qimenPan.zhishi}`); + } + + } catch (error) { + this.recordError('奇门起局', testCase.desc, error.message); + } + } + } + + // 格局识别测试 + async testPatternRecognition() { + console.log('🎯 测试格局识别功能...'); + + try { + // 创建测试用的奇门盘 + const testDate = new Date(2024, 2, 15, 10, 30); + const qimenPan = this.qimenAnalyzer.calculator.calculateQimenPan(testDate); + + // 分析格局 + const patterns = this.qimenAnalyzer.patternAnalyzer.analyzePatterns(qimenPan); + + // 验证格局分析结果 + this.assert( + Array.isArray(patterns), + '格局分析返回数组' + ); + + // 验证格局数据结构 + if (patterns.length > 0) { + const pattern = patterns[0]; + this.assert( + pattern.hasOwnProperty('name') && pattern.hasOwnProperty('type'), + '格局数据结构正确' + ); + } + + if (TEST_CONFIG.verbose) { + console.log(` ✅ 识别到 ${patterns.length} 个格局`); + patterns.slice(0, 3).forEach(pattern => { + console.log(` - ${pattern.name || '未知格局'}: ${pattern.level || '未知等级'}`); + }); + } + + } catch (error) { + this.recordError('格局识别', '基础测试', error.message); + } + } + + // 用神分析测试 + async testYongShenAnalysis() { + console.log('⚡ 测试用神分析功能...'); + + const testQuestions = [ + { question: '今年的财运如何?', type: '求财' }, + { question: '什么时候能结婚?', type: '婚姻' }, + { question: '身体健康状况如何?', type: '疾病' }, + { question: '工作能否顺利?', type: '求职' } + ]; + + for (const testCase of testQuestions) { + try { + const testDate = new Date(2024, 2, 15, 10, 30); + const qimenPan = this.qimenAnalyzer.calculator.calculateQimenPan(testDate); + + // 选择用神 + const yongshen = this.qimenAnalyzer.yongShenAnalyzer.selectYongShen( + testCase.question, + null, + qimenPan + ); + + // 验证用神选择 + this.assert( + typeof yongshen === 'object' && yongshen !== null, + `用神选择成功 - ${testCase.type}` + ); + + // 分析用神 + const analysis = this.qimenAnalyzer.yongShenAnalyzer.analyzeYongShen( + yongshen, + qimenPan + ); + + // 验证用神分析 + this.assert( + typeof analysis === 'object' && analysis !== null, + `用神分析成功 - ${testCase.type}` + ); + + if (TEST_CONFIG.verbose) { + console.log(` ✅ ${testCase.type}用神分析完成,用神数量: ${Object.keys(yongshen).length}`); + } + + } catch (error) { + this.recordError('用神分析', testCase.type, error.message); + } + } + } + + // 预测生成测试 + async testPredictionGeneration() { + console.log('🔮 测试预测生成功能...'); + + try { + const testDate = new Date(2024, 2, 15, 10, 30); + const qimenPan = this.qimenAnalyzer.calculator.calculateQimenPan(testDate); + const question = '今年的事业发展如何?'; + + // 选择和分析用神 + const yongshen = this.qimenAnalyzer.yongShenAnalyzer.selectYongShen( + question, + null, + qimenPan + ); + + const yongShenAnalysis = this.qimenAnalyzer.yongShenAnalyzer.analyzeYongShen( + yongshen, + qimenPan + ); + + // 分析格局 + const patterns = this.qimenAnalyzer.patternAnalyzer.analyzePatterns(qimenPan); + + // 生成预测 + const prediction = this.qimenAnalyzer.predictionGenerator.generatePrediction( + qimenPan, + yongShenAnalysis, + question, + patterns + ); + + // 验证预测结果 + this.assert( + prediction && typeof prediction === 'object', + '预测结果生成成功' + ); + + this.assert( + prediction.overall && typeof prediction.overall === 'string', + '总体预测存在' + ); + + this.assert( + typeof prediction.probability === 'number' && + prediction.probability >= 0 && + prediction.probability <= 100, + '成功概率有效(0-100)' + ); + + this.assert( + Array.isArray(prediction.details), + '详细分析为数组' + ); + + this.assert( + Array.isArray(prediction.suggestions), + '建议为数组' + ); + + if (TEST_CONFIG.verbose) { + console.log(` ✅ 预测生成成功`); + console.log(` 总体: ${prediction.overall}`); + console.log(` 概率: ${prediction.probability}%`); + console.log(` 详情数量: ${prediction.details.length}`); + console.log(` 建议数量: ${prediction.suggestions.length}`); + } + + } catch (error) { + this.recordError('预测生成', '基础测试', error.message); + } + } + + // 断言方法 + assert(condition, message) { + if (condition) { + this.testResults.passed++; + if (TEST_CONFIG.verbose) { + // console.log(` ✅ ${message}`); + } + } else { + this.testResults.failed++; + console.log(` ❌ ${message}`); + this.testResults.errors.push({ + test: message, + error: '断言失败' + }); + } + } + + // 记录错误 + recordError(testType, testCase, errorMessage) { + this.testResults.failed++; + this.testResults.errors.push({ + test: `${testType} - ${testCase}`, + error: errorMessage + }); + console.log(` ❌ ${testType} - ${testCase}: ${errorMessage}`); + } + + // 打印测试结果 + printTestResults() { + console.log('\n📊 测试结果汇总:'); + console.log(`✅ 通过: ${this.testResults.passed}`); + console.log(`❌ 失败: ${this.testResults.failed}`); + console.log(`📈 成功率: ${((this.testResults.passed / (this.testResults.passed + this.testResults.failed)) * 100).toFixed(2)}%`); + + if (this.testResults.errors.length > 0) { + console.log('\n❌ 错误详情:'); + this.testResults.errors.forEach((error, index) => { + console.log(`${index + 1}. ${error.test}: ${error.error}`); + }); + } + + console.log('\n🎉 奇门遁甲核心功能测试完成!'); + } +} + +// 运行测试 +if (require.main === module) { + const tester = new QimenCoreTest(); + tester.runAllTests().catch(error => { + console.error('测试执行失败:', error); + process.exit(1); + }); +} + +module.exports = QimenCoreTest; \ No newline at end of file