mirror of
https://github.com/patdelphi/suanming.git
synced 2026-02-27 21:23:12 +08:00
feat: 完整实现奇门遁甲功能并优化显示效果
主要功能实现: - 新增奇门遁甲分析完整功能模块 - 实现奇门盘可视化展示 - 添加用神分析、格局识别、预测结果等核心功能 - 集成AI解读和PDF导出功能 - 扩展历史记录支持奇门遁甲类型 显示优化: - 修复时机评估[object Object]显示问题 - 优化时机评估显示为简洁格式 - 完善英文字段中文化映射 - 移除重复的成功概率显示 - 统一数值显示格式(小数转整数) 技术改进: - 扩展类型定义支持奇门遁甲 - 完善API接口和路由 - 优化错误处理和用户体验 - 统一前后端字段映射机制
This commit is contained in:
@@ -30,7 +30,7 @@ CREATE TABLE IF NOT EXISTS user_profiles (
|
|||||||
CREATE TABLE IF NOT EXISTS numerology_readings (
|
CREATE TABLE IF NOT EXISTS numerology_readings (
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
user_id INTEGER NOT NULL,
|
user_id INTEGER NOT NULL,
|
||||||
reading_type TEXT NOT NULL CHECK (reading_type IN ('bazi', 'ziwei', 'yijing', 'wuxing')),
|
reading_type TEXT NOT NULL CHECK (reading_type IN ('bazi', 'ziwei', 'yijing', 'wuxing', 'qimen')),
|
||||||
name TEXT,
|
name TEXT,
|
||||||
birth_date TEXT,
|
birth_date TEXT,
|
||||||
birth_time TEXT,
|
birth_time TEXT,
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ const historyRoutes = require('./routes/history.cjs');
|
|||||||
const profileRoutes = require('./routes/profile.cjs');
|
const profileRoutes = require('./routes/profile.cjs');
|
||||||
const downloadRoutes = require('./routes/download.cjs');
|
const downloadRoutes = require('./routes/download.cjs');
|
||||||
const aiInterpretationRoutes = require('./routes/aiInterpretation.cjs');
|
const aiInterpretationRoutes = require('./routes/aiInterpretation.cjs');
|
||||||
|
const qimenRoutes = require('./routes/qimen.cjs');
|
||||||
|
|
||||||
// 导入中间件
|
// 导入中间件
|
||||||
const { errorHandler } = require('./middleware/errorHandler.cjs');
|
const { errorHandler } = require('./middleware/errorHandler.cjs');
|
||||||
@@ -129,6 +130,7 @@ app.use('/api/history', historyRoutes);
|
|||||||
app.use('/api/profile', profileRoutes);
|
app.use('/api/profile', profileRoutes);
|
||||||
app.use('/api/download', downloadRoutes);
|
app.use('/api/download', downloadRoutes);
|
||||||
app.use('/api/ai-interpretation', aiInterpretationRoutes);
|
app.use('/api/ai-interpretation', aiInterpretationRoutes);
|
||||||
|
app.use('/api/qimen', qimenRoutes);
|
||||||
|
|
||||||
// 静态文件服务 (用于生产环境)
|
// 静态文件服务 (用于生产环境)
|
||||||
// 强制在 Koyeb 部署时启用静态文件服务
|
// 强制在 Koyeb 部署时启用静态文件服务
|
||||||
|
|||||||
@@ -216,7 +216,7 @@ router.post('/save-history', authenticate, asyncHandler(async (req, res) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 验证分析类型
|
// 验证分析类型
|
||||||
const validTypes = ['bazi', 'ziwei', 'yijing'];
|
const validTypes = ['bazi', 'ziwei', 'yijing', 'qimen'];
|
||||||
if (!validTypes.includes(analysis_type)) {
|
if (!validTypes.includes(analysis_type)) {
|
||||||
throw new AppError('无效的分析类型', 400, 'INVALID_ANALYSIS_TYPE');
|
throw new AppError('无效的分析类型', 400, 'INVALID_ANALYSIS_TYPE');
|
||||||
}
|
}
|
||||||
@@ -236,6 +236,15 @@ router.post('/save-history', authenticate, asyncHandler(async (req, res) => {
|
|||||||
birth_time = null;
|
birth_time = null;
|
||||||
birth_place = null;
|
birth_place = null;
|
||||||
gender = null;
|
gender = null;
|
||||||
|
} else if (analysis_type === 'qimen') {
|
||||||
|
// 奇门遁甲:获取用户档案信息
|
||||||
|
const getUserProfile = db.prepare('SELECT full_name FROM user_profiles WHERE user_id = ?');
|
||||||
|
const userProfile = getUserProfile.get(req.user.id);
|
||||||
|
name = userProfile?.full_name || '奇门遁甲用户';
|
||||||
|
birth_date = input_data?.birth_date || null;
|
||||||
|
birth_time = input_data?.birth_time || null;
|
||||||
|
birth_place = null;
|
||||||
|
gender = null;
|
||||||
} else {
|
} else {
|
||||||
// 八字和紫微:从输入数据中获取
|
// 八字和紫微:从输入数据中获取
|
||||||
name = input_data?.name || '用户';
|
name = input_data?.name || '用户';
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ router.post('/', authenticate, async (req, res) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 验证分析类型
|
// 验证分析类型
|
||||||
const supportedAnalysisTypes = ['bazi', 'ziwei', 'yijing'];
|
const supportedAnalysisTypes = ['bazi', 'ziwei', 'yijing', 'qimen'];
|
||||||
if (!supportedAnalysisTypes.includes(analysisType)) {
|
if (!supportedAnalysisTypes.includes(analysisType)) {
|
||||||
return res.status(400).json({
|
return res.status(400).json({
|
||||||
error: '不支持的分析类型',
|
error: '不支持的分析类型',
|
||||||
|
|||||||
@@ -8,6 +8,72 @@ const logger = require('../middleware/logger.cjs');
|
|||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
const qimenAnalyzer = new QimenAnalyzer();
|
const qimenAnalyzer = new QimenAnalyzer();
|
||||||
|
const { authenticate } = require('../middleware/auth.cjs');
|
||||||
|
const { getDB } = require('../database/index.cjs');
|
||||||
|
const { AppError, asyncHandler } = require('../middleware/errorHandler.cjs');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @route POST /api/qimen/analyze
|
||||||
|
* @desc 奇门遁甲完整分析
|
||||||
|
* @access Private
|
||||||
|
*/
|
||||||
|
router.post('/analyze', asyncHandler(async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { question, birth_date, birth_time, user_timezone, local_time, user_id } = req.body;
|
||||||
|
const userId = user_id || 1; // 测试用户ID
|
||||||
|
|
||||||
|
// 输入验证
|
||||||
|
if (!question || typeof question !== 'string' || question.trim().length === 0) {
|
||||||
|
throw new AppError('缺少必要参数:占卜问题不能为空', 400, 'MISSING_QUESTION');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!birth_date || typeof birth_date !== 'string') {
|
||||||
|
throw new AppError('缺少必要参数:出生日期', 400, 'MISSING_BIRTH_DATE');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!birth_time || typeof birth_time !== 'string') {
|
||||||
|
throw new AppError('缺少必要参数:出生时间', 400, 'MISSING_BIRTH_TIME');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建分析数据
|
||||||
|
const analysisData = {
|
||||||
|
question: question.trim(),
|
||||||
|
birth_date,
|
||||||
|
birth_time,
|
||||||
|
user_timezone: user_timezone || 'Asia/Shanghai',
|
||||||
|
local_time: local_time || new Date().toISOString()
|
||||||
|
};
|
||||||
|
|
||||||
|
// 执行奇门遁甲分析
|
||||||
|
const analysisResult = qimenAnalyzer.performQimenAnalysis(analysisData);
|
||||||
|
|
||||||
|
// 保存到历史记录
|
||||||
|
const db = getDB();
|
||||||
|
const insertReading = db.prepare(`
|
||||||
|
INSERT INTO numerology_readings (
|
||||||
|
user_id, reading_type, input_data, analysis, created_at
|
||||||
|
) VALUES (?, ?, ?, ?, datetime('now'))
|
||||||
|
`);
|
||||||
|
|
||||||
|
const result = insertReading.run(
|
||||||
|
userId,
|
||||||
|
'qimen',
|
||||||
|
JSON.stringify(analysisData),
|
||||||
|
JSON.stringify(analysisResult)
|
||||||
|
);
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
data: {
|
||||||
|
record_id: result.lastInsertRowid,
|
||||||
|
analysis: analysisResult
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('奇门遁甲分析错误:', error);
|
||||||
|
throw new AppError(`奇门遁甲分析过程中发生错误: ${error.message}`, 500, 'QIMEN_ANALYSIS_ERROR');
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @route POST /api/qimen/calculate
|
* @route POST /api/qimen/calculate
|
||||||
@@ -536,7 +602,7 @@ router.post('/batch-calculate', async (req, res) => {
|
|||||||
|
|
||||||
// 错误处理中间件
|
// 错误处理中间件
|
||||||
router.use((error, req, res, next) => {
|
router.use((error, req, res, next) => {
|
||||||
logger.error('奇门API错误', error);
|
console.error('奇门API错误:', error);
|
||||||
|
|
||||||
res.status(500).json({
|
res.status(500).json({
|
||||||
success: false,
|
success: false,
|
||||||
|
|||||||
@@ -18,6 +18,9 @@ const generateMarkdown = async (analysisData, analysisType, userName) => {
|
|||||||
case 'yijing':
|
case 'yijing':
|
||||||
markdown = generateYijingMarkdown(analysisData, userName);
|
markdown = generateYijingMarkdown(analysisData, userName);
|
||||||
break;
|
break;
|
||||||
|
case 'qimen':
|
||||||
|
markdown = generateQimenMarkdown(analysisData, userName);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Error(`不支持的分析类型: ${analysisType}`);
|
throw new Error(`不支持的分析类型: ${analysisType}`);
|
||||||
}
|
}
|
||||||
@@ -2291,6 +2294,458 @@ const generateYijingMarkdown = (analysisData, userName) => {
|
|||||||
return markdown;
|
return markdown;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成奇门遁甲Markdown文档
|
||||||
|
*/
|
||||||
|
const generateQimenMarkdown = (analysisData, userName) => {
|
||||||
|
const timestamp = new Date().toLocaleString('zh-CN');
|
||||||
|
|
||||||
|
// 字段名称中文映射
|
||||||
|
const fieldNameMap = {
|
||||||
|
'question': '问题',
|
||||||
|
'method': '起局方法',
|
||||||
|
'divination_time': '起局时间',
|
||||||
|
'jieqi': '节气',
|
||||||
|
'yuan': '元',
|
||||||
|
'jushu': '局数',
|
||||||
|
'yindun': '阴阳遁',
|
||||||
|
'ganzhi': '干支',
|
||||||
|
'year': '年柱',
|
||||||
|
'month': '月柱',
|
||||||
|
'day': '日柱',
|
||||||
|
'hour': '时柱',
|
||||||
|
'gan': '干',
|
||||||
|
'zhi': '支',
|
||||||
|
'zhifu': '值符',
|
||||||
|
'zhishi': '值使',
|
||||||
|
'star': '九星',
|
||||||
|
'door': '八门',
|
||||||
|
'god': '八神',
|
||||||
|
'primary': '主用神',
|
||||||
|
'secondary': '次用神',
|
||||||
|
'overall': '综合评估',
|
||||||
|
'favorability': '有利度',
|
||||||
|
'strength': '力量强度',
|
||||||
|
'timing': '时机评估',
|
||||||
|
'recommendation': '建议',
|
||||||
|
'element': '天干',
|
||||||
|
'position': '宫位',
|
||||||
|
'palaceName': '宫位名称',
|
||||||
|
'wangshui': '旺衰',
|
||||||
|
'wangshuiScore': '旺衰评分',
|
||||||
|
'palaceRelation': '宫位关系',
|
||||||
|
'palaceHarmony': '宫位和谐度',
|
||||||
|
'seasonalInfluence': '季节影响',
|
||||||
|
'seasonalScore': '季节评分',
|
||||||
|
'totalScore': '综合评分',
|
||||||
|
'status': '状态',
|
||||||
|
'description': '详细描述',
|
||||||
|
'name': '名称',
|
||||||
|
'type': '类型',
|
||||||
|
'level': '等级',
|
||||||
|
'influence': '影响',
|
||||||
|
'probability': '成功概率',
|
||||||
|
'analysis': '详细分析',
|
||||||
|
'key_factors': '关键因素',
|
||||||
|
'timing_advice': '时机建议',
|
||||||
|
'action_suggestions': '行动建议',
|
||||||
|
'precautions': '注意事项',
|
||||||
|
'wuxing_analysis': '五行分析',
|
||||||
|
'timing_analysis': '时机分析',
|
||||||
|
'zhifuAnalysis': '值符分析',
|
||||||
|
'zhishiAnalysis': '值使分析',
|
||||||
|
'hourAnalysis': '时辰分析',
|
||||||
|
'seasonAnalysis': '节气分析',
|
||||||
|
'yindunAnalysis': '阴阳遁分析',
|
||||||
|
'score': '评分',
|
||||||
|
'factors': '影响因素',
|
||||||
|
// 财运相关字段
|
||||||
|
'profit': '利润',
|
||||||
|
'investment': '投资',
|
||||||
|
'wealth': '财富',
|
||||||
|
'money': '金钱',
|
||||||
|
'finance': '财务',
|
||||||
|
'business': '生意',
|
||||||
|
'career': '事业',
|
||||||
|
'work': '工作',
|
||||||
|
'job': '职业',
|
||||||
|
'success': '成功',
|
||||||
|
'failure': '失败',
|
||||||
|
'opportunity': '机会',
|
||||||
|
'risk': '风险',
|
||||||
|
'challenge': '挑战',
|
||||||
|
'advantage': '优势',
|
||||||
|
'disadvantage': '劣势',
|
||||||
|
// 用神分析字段
|
||||||
|
'matter': '事情',
|
||||||
|
'result': '结果',
|
||||||
|
'self': '自身',
|
||||||
|
'opponent': '对手',
|
||||||
|
'helper': '帮助者',
|
||||||
|
'obstacle': '阻碍',
|
||||||
|
// 五行分析字段
|
||||||
|
'dominant': '主导五行',
|
||||||
|
'balance': '平衡状态',
|
||||||
|
'suggestions': '建议',
|
||||||
|
'notes': '备注',
|
||||||
|
'season': '季节',
|
||||||
|
// 时机分析字段
|
||||||
|
'favorable': '有利',
|
||||||
|
'unfavorable': '不利',
|
||||||
|
'neutral': '中性',
|
||||||
|
// 其他常见字段
|
||||||
|
'true': '是',
|
||||||
|
'false': '否',
|
||||||
|
'unknown': '未知',
|
||||||
|
'good': '好',
|
||||||
|
'bad': '差',
|
||||||
|
'excellent': '极佳',
|
||||||
|
'poor': '很差',
|
||||||
|
'average': '一般',
|
||||||
|
// 感情相关字段
|
||||||
|
'spouse': '配偶',
|
||||||
|
'relationship': '感情关系',
|
||||||
|
'matchmaker': '媒人',
|
||||||
|
'marriage_palace': '婚姻宫',
|
||||||
|
'relationship_door': '感情门',
|
||||||
|
'love': '爱情',
|
||||||
|
'marriage': '婚姻',
|
||||||
|
'partner': '伴侣',
|
||||||
|
'emotion': '情感',
|
||||||
|
'affection': '感情',
|
||||||
|
'romance': '浪漫',
|
||||||
|
'compatibility': '相配度',
|
||||||
|
'harmony': '和谐度',
|
||||||
|
'conflict': '冲突',
|
||||||
|
'separation': '分离',
|
||||||
|
'reunion': '复合',
|
||||||
|
'commitment': '承诺',
|
||||||
|
'trust': '信任',
|
||||||
|
'loyalty': '忠诚'
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取中文字段名
|
||||||
|
const getChineseFieldName = (fieldName) => {
|
||||||
|
return fieldNameMap[fieldName] || fieldName;
|
||||||
|
};
|
||||||
|
|
||||||
|
let markdown = `# 奇门遁甲分析报告\n\n`;
|
||||||
|
// 从userName中提取实际姓名,去掉"奇门_"前缀
|
||||||
|
const actualUserName = userName ? userName.replace(/^奇门_/, '') : '用户';
|
||||||
|
markdown += `**占卜者:** ${actualUserName}\n`;
|
||||||
|
markdown += `**生成时间:** ${timestamp}\n`;
|
||||||
|
markdown += `**分析类型:** 奇门遁甲\n\n`;
|
||||||
|
|
||||||
|
markdown += `---\n\n`;
|
||||||
|
|
||||||
|
// 占卜问题
|
||||||
|
if (analysisData.basic_info?.divination_data) {
|
||||||
|
markdown += `## ❓ 占卜问题\n\n`;
|
||||||
|
const divination = analysisData.basic_info.divination_data;
|
||||||
|
if (divination.question) {
|
||||||
|
markdown += `**问题:** ${divination.question}\n\n`;
|
||||||
|
}
|
||||||
|
if (divination.method) {
|
||||||
|
markdown += `**起局方法:** ${divination.method}\n\n`;
|
||||||
|
}
|
||||||
|
if (divination.divination_time) {
|
||||||
|
const time = new Date(divination.divination_time).toLocaleString('zh-CN');
|
||||||
|
markdown += `**起局时间:** ${time}\n\n`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 时空信息
|
||||||
|
if (analysisData.basic_info?.qimen_info) {
|
||||||
|
markdown += `## ⏰ 时空信息\n\n`;
|
||||||
|
const qimenInfo = analysisData.basic_info.qimen_info;
|
||||||
|
|
||||||
|
if (qimenInfo.jieqi) {
|
||||||
|
markdown += `**节气:** ${qimenInfo.jieqi}\n`;
|
||||||
|
}
|
||||||
|
if (qimenInfo.yuan) {
|
||||||
|
markdown += `**元:** ${qimenInfo.yuan}\n`;
|
||||||
|
}
|
||||||
|
if (qimenInfo.jushu) {
|
||||||
|
markdown += `**局数:** ${qimenInfo.jushu}局\n`;
|
||||||
|
}
|
||||||
|
if (qimenInfo.yindun !== undefined) {
|
||||||
|
markdown += `**阴阳遁:** ${qimenInfo.yindun ? '阴遁' : '阳遁'}\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 干支四柱
|
||||||
|
if (qimenInfo.ganzhi) {
|
||||||
|
markdown += `\n### 🎋 干支四柱\n\n`;
|
||||||
|
const ganzhi = qimenInfo.ganzhi;
|
||||||
|
if (ganzhi.year) markdown += `- **年柱:** ${ganzhi.year.gan}${ganzhi.year.zhi}\n`;
|
||||||
|
if (ganzhi.month) markdown += `- **月柱:** ${ganzhi.month.gan}${ganzhi.month.zhi}\n`;
|
||||||
|
if (ganzhi.day) markdown += `- **日柱:** ${ganzhi.day.gan}${ganzhi.day.zhi}\n`;
|
||||||
|
if (ganzhi.hour) markdown += `- **时柱:** ${ganzhi.hour.gan}${ganzhi.hour.zhi}\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 值符值使
|
||||||
|
if (qimenInfo.zhifu || qimenInfo.zhishi) {
|
||||||
|
markdown += `\n### ⭐ 值符值使\n\n`;
|
||||||
|
if (qimenInfo.zhifu) markdown += `- **值符:** ${qimenInfo.zhifu}\n`;
|
||||||
|
if (qimenInfo.zhishi) markdown += `- **值使:** ${qimenInfo.zhishi}\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
markdown += `\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 奇门盘布局
|
||||||
|
if (analysisData.detailed_analysis?.qimen_pan) {
|
||||||
|
markdown += `## 🔮 奇门盘布局\n\n`;
|
||||||
|
const qimenPan = analysisData.detailed_analysis.qimen_pan;
|
||||||
|
|
||||||
|
if (qimenPan.dipan && Array.isArray(qimenPan.dipan)) {
|
||||||
|
const palaceNames = ['坎一宫', '坤二宫', '震三宫', '巽四宫', '中五宫', '乾六宫', '兑七宫', '艮八宫', '离九宫'];
|
||||||
|
|
||||||
|
markdown += `| 宫位 | 九星 | 八门 | 八神 |\n`;
|
||||||
|
markdown += `|------|------|------|------|\n`;
|
||||||
|
|
||||||
|
qimenPan.dipan.forEach((palace, index) => {
|
||||||
|
if (palace && palaceNames[index]) {
|
||||||
|
const star = palace.star || '-';
|
||||||
|
const door = palace.door || '-';
|
||||||
|
const god = palace.god || '-';
|
||||||
|
markdown += `| ${palaceNames[index]} | ${star} | ${door} | ${god} |\n`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
markdown += `\n`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 用神分析
|
||||||
|
if (analysisData.detailed_analysis?.yongshen_analysis) {
|
||||||
|
markdown += `## 🎯 用神分析\n\n`;
|
||||||
|
const yongShenAnalysis = analysisData.detailed_analysis.yongshen_analysis;
|
||||||
|
|
||||||
|
// 主用神
|
||||||
|
if (yongShenAnalysis.primary) {
|
||||||
|
markdown += `### 主用神\n\n`;
|
||||||
|
Object.entries(yongShenAnalysis.primary).forEach(([key, value]) => {
|
||||||
|
const chineseKey = getChineseFieldName(key);
|
||||||
|
if (typeof value === 'object' && value !== null) {
|
||||||
|
markdown += `**${chineseKey}:**\n`;
|
||||||
|
Object.entries(value).forEach(([subKey, subValue]) => {
|
||||||
|
const chineseSubKey = getChineseFieldName(subKey);
|
||||||
|
markdown += `- ${chineseSubKey}:${subValue}\n`;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
markdown += `- **${chineseKey}:** ${value}\n`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
markdown += `\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 次用神
|
||||||
|
if (yongShenAnalysis.secondary) {
|
||||||
|
markdown += `### 次用神\n\n`;
|
||||||
|
Object.entries(yongShenAnalysis.secondary).forEach(([key, value]) => {
|
||||||
|
const chineseKey = getChineseFieldName(key);
|
||||||
|
if (typeof value === 'object' && value !== null) {
|
||||||
|
markdown += `**${chineseKey}:**\n`;
|
||||||
|
Object.entries(value).forEach(([subKey, subValue]) => {
|
||||||
|
const chineseSubKey = getChineseFieldName(subKey);
|
||||||
|
markdown += `- ${chineseSubKey}:${subValue}\n`;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
markdown += `- **${chineseKey}:** ${value}\n`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
markdown += `\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 综合评估
|
||||||
|
if (yongShenAnalysis.overall) {
|
||||||
|
markdown += `### 用神综合评估\n\n`;
|
||||||
|
if (typeof yongShenAnalysis.overall === 'object') {
|
||||||
|
Object.entries(yongShenAnalysis.overall).forEach(([key, value]) => {
|
||||||
|
const chineseKey = getChineseFieldName(key);
|
||||||
|
if (typeof value === 'object' && value !== null) {
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
markdown += `- **${chineseKey}:** ${value.join('、')}\n`;
|
||||||
|
} else {
|
||||||
|
const subEntries = Object.entries(value).map(([subK, subV]) => {
|
||||||
|
const chineseSubKey = getChineseFieldName(subK);
|
||||||
|
return `${chineseSubKey}:${subV}`;
|
||||||
|
}).join(';');
|
||||||
|
markdown += `- **${chineseKey}:** ${subEntries}\n`;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
markdown += `- **${chineseKey}:** ${value}\n`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
markdown += `${yongShenAnalysis.overall}\n`;
|
||||||
|
}
|
||||||
|
markdown += `\n`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 格局识别
|
||||||
|
if (analysisData.detailed_analysis?.pattern_analysis && Array.isArray(analysisData.detailed_analysis.pattern_analysis)) {
|
||||||
|
markdown += `## ⭐ 格局识别\n\n`;
|
||||||
|
|
||||||
|
const patterns = analysisData.detailed_analysis.pattern_analysis;
|
||||||
|
const auspiciousPatterns = patterns.filter(p => p.type === 'auspicious');
|
||||||
|
const inauspiciousPatterns = patterns.filter(p => p.type === 'inauspicious');
|
||||||
|
const neutralPatterns = patterns.filter(p => p.type === 'neutral');
|
||||||
|
|
||||||
|
if (auspiciousPatterns.length > 0) {
|
||||||
|
markdown += `### 🌟 吉利格局\n\n`;
|
||||||
|
auspiciousPatterns.forEach(pattern => {
|
||||||
|
markdown += `**${pattern.name}** (${pattern.level || '吉'})\n`;
|
||||||
|
if (pattern.description) markdown += `${pattern.description}\n`;
|
||||||
|
if (pattern.influence) markdown += `**影响:** ${pattern.influence}\n`;
|
||||||
|
markdown += `\n`;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (neutralPatterns.length > 0) {
|
||||||
|
markdown += `### ⚖️ 中性格局\n\n`;
|
||||||
|
neutralPatterns.forEach(pattern => {
|
||||||
|
markdown += `**${pattern.name}** (${pattern.level || '中'})\n`;
|
||||||
|
if (pattern.description) markdown += `${pattern.description}\n`;
|
||||||
|
if (pattern.influence) markdown += `**影响:** ${pattern.influence}\n`;
|
||||||
|
markdown += `\n`;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inauspiciousPatterns.length > 0) {
|
||||||
|
markdown += `### ⚠️ 不利格局\n\n`;
|
||||||
|
inauspiciousPatterns.forEach(pattern => {
|
||||||
|
markdown += `**${pattern.name}** (${pattern.level || '凶'})\n`;
|
||||||
|
if (pattern.description) markdown += `${pattern.description}\n`;
|
||||||
|
if (pattern.influence) markdown += `**影响:** ${pattern.influence}\n`;
|
||||||
|
markdown += `\n`;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 预测结果
|
||||||
|
if (analysisData.prediction_result) {
|
||||||
|
markdown += `## 🔮 预测结果\n\n`;
|
||||||
|
const prediction = analysisData.prediction_result;
|
||||||
|
|
||||||
|
if (prediction.probability !== undefined) {
|
||||||
|
markdown += `### 成功概率\n\n`;
|
||||||
|
markdown += `**概率:** ${prediction.probability}%\n\n`;
|
||||||
|
|
||||||
|
let probabilityLevel = '';
|
||||||
|
if (prediction.probability >= 80) probabilityLevel = '极高';
|
||||||
|
else if (prediction.probability >= 60) probabilityLevel = '较高';
|
||||||
|
else if (prediction.probability >= 40) probabilityLevel = '中等';
|
||||||
|
else if (prediction.probability >= 20) probabilityLevel = '较低';
|
||||||
|
else probabilityLevel = '很低';
|
||||||
|
|
||||||
|
markdown += `**评级:** ${probabilityLevel}\n\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prediction.analysis) {
|
||||||
|
markdown += `### 详细分析\n\n`;
|
||||||
|
if (typeof prediction.analysis === 'object') {
|
||||||
|
Object.entries(prediction.analysis).forEach(([key, value]) => {
|
||||||
|
const chineseKey = getChineseFieldName(key);
|
||||||
|
markdown += `**${chineseKey}:** ${value}\n`;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
markdown += `${prediction.analysis}\n`;
|
||||||
|
}
|
||||||
|
markdown += `\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prediction.key_factors) {
|
||||||
|
markdown += `### 关键因素\n\n`;
|
||||||
|
if (typeof prediction.key_factors === 'object') {
|
||||||
|
Object.entries(prediction.key_factors).forEach(([factor, impact]) => {
|
||||||
|
const chineseFactor = getChineseFieldName(factor);
|
||||||
|
markdown += `- **${chineseFactor}:** ${impact}\n`;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
markdown += `${prediction.key_factors}\n`;
|
||||||
|
}
|
||||||
|
markdown += `\n`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 指导建议
|
||||||
|
if (analysisData.guidance) {
|
||||||
|
markdown += `## 💡 指导建议\n\n`;
|
||||||
|
const guidance = analysisData.guidance;
|
||||||
|
|
||||||
|
if (guidance.timing_advice) {
|
||||||
|
markdown += `### ⏰ 时机建议\n\n`;
|
||||||
|
if (typeof guidance.timing_advice === 'object') {
|
||||||
|
Object.entries(guidance.timing_advice).forEach(([key, value]) => {
|
||||||
|
const chineseKey = getChineseFieldName(key);
|
||||||
|
markdown += `**${chineseKey}:** ${value}\n`;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
markdown += `${guidance.timing_advice}\n`;
|
||||||
|
}
|
||||||
|
markdown += `\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (guidance.action_suggestions && Array.isArray(guidance.action_suggestions)) {
|
||||||
|
markdown += `### 🎯 行动建议\n\n`;
|
||||||
|
guidance.action_suggestions.forEach(suggestion => {
|
||||||
|
markdown += `- ${suggestion}\n`;
|
||||||
|
});
|
||||||
|
markdown += `\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (guidance.precautions && Array.isArray(guidance.precautions)) {
|
||||||
|
markdown += `### ⚠️ 注意事项\n\n`;
|
||||||
|
guidance.precautions.forEach(precaution => {
|
||||||
|
markdown += `- ${precaution}\n`;
|
||||||
|
});
|
||||||
|
markdown += `\n`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 五行分析
|
||||||
|
if (analysisData.detailed_analysis?.wuxing_analysis) {
|
||||||
|
markdown += `## 🌟 五行分析\n\n`;
|
||||||
|
const wuxingAnalysis = analysisData.detailed_analysis.wuxing_analysis;
|
||||||
|
|
||||||
|
if (typeof wuxingAnalysis === 'object') {
|
||||||
|
Object.entries(wuxingAnalysis).forEach(([key, value]) => {
|
||||||
|
const chineseKey = getChineseFieldName(key);
|
||||||
|
markdown += `**${chineseKey}:** ${value}\n`;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
markdown += `${wuxingAnalysis}\n`;
|
||||||
|
}
|
||||||
|
markdown += `\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 时机分析
|
||||||
|
if (analysisData.detailed_analysis?.timing_analysis) {
|
||||||
|
markdown += `## ⏰ 时机分析\n\n`;
|
||||||
|
const timingAnalysis = analysisData.detailed_analysis.timing_analysis;
|
||||||
|
|
||||||
|
if (typeof timingAnalysis === 'object') {
|
||||||
|
Object.entries(timingAnalysis).forEach(([key, value]) => {
|
||||||
|
const chineseKey = getChineseFieldName(key);
|
||||||
|
markdown += `**${chineseKey}:** ${value}\n`;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
markdown += `${timingAnalysis}\n`;
|
||||||
|
}
|
||||||
|
markdown += `\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 页脚
|
||||||
|
markdown += `---\n\n`;
|
||||||
|
markdown += `*本报告由神机阁AI命理分析平台生成*\n`;
|
||||||
|
markdown += `*生成时间:${timestamp}*\n`;
|
||||||
|
markdown += `*仅供参考,请理性对待*\n`;
|
||||||
|
|
||||||
|
return markdown;
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
generateMarkdown
|
generateMarkdown
|
||||||
};
|
};
|
||||||
@@ -5,6 +5,7 @@ const AnalysisCache = require('./common/AnalysisCache.cjs');
|
|||||||
const EnhancedRandom = require('./common/EnhancedRandom.cjs');
|
const EnhancedRandom = require('./common/EnhancedRandom.cjs');
|
||||||
const TimeConverter = require('../utils/timeConverter.cjs');
|
const TimeConverter = require('../utils/timeConverter.cjs');
|
||||||
const SolarTerms = require('../utils/solarTerms.cjs');
|
const SolarTerms = require('../utils/solarTerms.cjs');
|
||||||
|
const BaziAnalyzer = require('./baziAnalyzer.cjs');
|
||||||
|
|
||||||
class QimenAnalyzer {
|
class QimenAnalyzer {
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -27,6 +28,9 @@ class QimenAnalyzer {
|
|||||||
// 初始化时间转换器
|
// 初始化时间转换器
|
||||||
this.timeConverter = new TimeConverter();
|
this.timeConverter = new TimeConverter();
|
||||||
|
|
||||||
|
// 初始化八字分析器(复用农历计算功能)
|
||||||
|
this.baziAnalyzer = new BaziAnalyzer();
|
||||||
|
|
||||||
// 初始化节气计算器
|
// 初始化节气计算器
|
||||||
this.solarTerms = new SolarTerms();
|
this.solarTerms = new SolarTerms();
|
||||||
|
|
||||||
@@ -334,19 +338,19 @@ class QimenAnalyzer {
|
|||||||
const currentTime = datetime ? new Date(datetime) : new Date();
|
const currentTime = datetime ? new Date(datetime) : new Date();
|
||||||
|
|
||||||
// 起局
|
// 起局
|
||||||
const qimenPan = this.calculateQimenPan(currentTime);
|
const qimenPan = this.calculator.calculateQimenPan(currentTime);
|
||||||
|
|
||||||
// 选择用神
|
// 选择用神
|
||||||
const yongShen = this.selectYongShen(question, birth_data, qimenPan);
|
const yongShen = this.yongShenAnalyzer.selectYongShen(question, birth_data, qimenPan);
|
||||||
|
|
||||||
// 格局分析
|
// 格局分析
|
||||||
const patterns = this.analyzePatterns(qimenPan);
|
const patterns = this.patternAnalyzer.analyzePatterns(qimenPan);
|
||||||
|
|
||||||
// 用神分析
|
// 用神分析
|
||||||
const yongShenAnalysis = this.analyzeYongShen(yongShen, qimenPan);
|
const yongShenAnalysis = this.yongShenAnalyzer.analyzeYongShen(yongShen, qimenPan);
|
||||||
|
|
||||||
// 生成预测结果
|
// 生成预测结果
|
||||||
const prediction = this.generatePrediction(qimenPan, yongShenAnalysis, question, patterns);
|
const prediction = this.predictionGenerator.generatePrediction(qimenPan, yongShenAnalysis, question, patterns);
|
||||||
|
|
||||||
const result = {
|
const result = {
|
||||||
analysis_type: 'qimen',
|
analysis_type: 'qimen',
|
||||||
@@ -359,17 +363,17 @@ class QimenAnalyzer {
|
|||||||
lunar_info: this.calculateLunarInfo(currentTime)
|
lunar_info: this.calculateLunarInfo(currentTime)
|
||||||
},
|
},
|
||||||
qimen_info: {
|
qimen_info: {
|
||||||
jieqi: qimenPan.timeInfo.jieqi,
|
jieqi: qimenPan.timeInfo?.jieqi || '未知',
|
||||||
yuan: qimenPan.timeInfo.yuan,
|
yuan: qimenPan.timeInfo?.yuan || '未知',
|
||||||
jushu: qimenPan.timeInfo.jushu,
|
jushu: qimenPan.timeInfo?.jushu || qimenPan.jushu || '未知',
|
||||||
yindun: qimenPan.timeInfo.yindun ? '阴遁' : '阳遁',
|
yindun: qimenPan.timeInfo?.yindun !== undefined ? (qimenPan.timeInfo.yindun ? '阴遁' : '阳遁') : (qimenPan.yindun ? '阴遁' : '阳遁'),
|
||||||
zhifu: qimenPan.zhifu,
|
zhifu: qimenPan.zhifu || '未知',
|
||||||
zhishi: qimenPan.zhishi,
|
zhishi: qimenPan.zhishi || '未知',
|
||||||
ganzhi: {
|
ganzhi: {
|
||||||
year: qimenPan.timeInfo.year,
|
year: qimenPan.timeInfo?.year || { gan: '未知', zhi: '未知' },
|
||||||
month: qimenPan.timeInfo.month,
|
month: qimenPan.timeInfo?.month || { gan: '未知', zhi: '未知' },
|
||||||
day: qimenPan.timeInfo.day,
|
day: qimenPan.timeInfo?.day || { gan: '未知', zhi: '未知' },
|
||||||
hour: qimenPan.timeInfo.hour
|
hour: qimenPan.timeInfo?.hour || { gan: '未知', zhi: '未知' }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -957,17 +961,56 @@ class QimenAnalyzer {
|
|||||||
* @returns {number} 成功概率(0-100)
|
* @returns {number} 成功概率(0-100)
|
||||||
*/
|
*/
|
||||||
calculateProbability(yongShenAnalysis, patterns) {
|
calculateProbability(yongShenAnalysis, patterns) {
|
||||||
let baseProbability = yongShenAnalysis.overall.favorability || 50;
|
let baseProbability = 50; // 基础概率
|
||||||
|
|
||||||
|
// 基于用神分析调整概率
|
||||||
|
if (yongShenAnalysis && yongShenAnalysis.overall) {
|
||||||
|
const favorability = yongShenAnalysis.overall.favorability;
|
||||||
|
if (typeof favorability === 'number') {
|
||||||
|
baseProbability = favorability;
|
||||||
|
} else {
|
||||||
|
// 如果没有favorability,基于用神旺衰计算
|
||||||
|
let yongShenScore = 0;
|
||||||
|
let yongShenCount = 0;
|
||||||
|
|
||||||
|
// 遍历所有用神分析
|
||||||
|
['primary', 'secondary', 'auxiliary'].forEach(category => {
|
||||||
|
if (yongShenAnalysis[category]) {
|
||||||
|
Object.values(yongShenAnalysis[category]).forEach(analysis => {
|
||||||
|
if (analysis && analysis.wangshui) {
|
||||||
|
yongShenCount++;
|
||||||
|
switch (analysis.wangshui) {
|
||||||
|
case '旺': yongShenScore += 20; break;
|
||||||
|
case '相': yongShenScore += 10; break;
|
||||||
|
case '休': yongShenScore += 0; break;
|
||||||
|
case '囚': yongShenScore -= 10; break;
|
||||||
|
case '死': yongShenScore -= 20; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (yongShenCount > 0) {
|
||||||
|
baseProbability = 50 + (yongShenScore / yongShenCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 根据格局调整概率
|
// 根据格局调整概率
|
||||||
if (patterns && patterns.length > 0) {
|
if (patterns && patterns.length > 0) {
|
||||||
const patternScore = patterns.reduce((sum, pattern) => sum + pattern.score, 0);
|
const patternScore = patterns.reduce((sum, pattern) => {
|
||||||
const patternAdjustment = Math.min(Math.max(patternScore, -20), 20);
|
const score = pattern.score || 0;
|
||||||
|
return sum + score;
|
||||||
|
}, 0);
|
||||||
|
|
||||||
|
// 格局影响权重调整
|
||||||
|
const patternAdjustment = Math.min(Math.max(patternScore * 0.8, -25), 25);
|
||||||
baseProbability += patternAdjustment;
|
baseProbability += patternAdjustment;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 确保概率在合理范围内
|
// 确保概率在合理范围内
|
||||||
return Math.min(Math.max(baseProbability, 10), 90);
|
return Math.min(Math.max(Math.round(baseProbability), 15), 85);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1032,15 +1075,34 @@ class QimenAnalyzer {
|
|||||||
return '不利,建议暂缓或改变策略';
|
return '不利,建议暂缓或改变策略';
|
||||||
}
|
}
|
||||||
|
|
||||||
// 辅助方法:计算农历信息
|
// 辅助方法:计算农历信息(复用八字分析器的农历算法)
|
||||||
calculateLunarInfo(datetime) {
|
calculateLunarInfo(datetime) {
|
||||||
// 简化实现,实际应用中需要更精确的农历转换
|
try {
|
||||||
return {
|
// 将datetime转换为日期字符串格式
|
||||||
year: '甲辰',
|
const date = new Date(datetime);
|
||||||
month: '丁卯',
|
const dateStr = date.toISOString().split('T')[0]; // YYYY-MM-DD格式
|
||||||
day: '庚申',
|
|
||||||
description: '农历信息(简化版)'
|
// 复用八字分析器的农历计算功能
|
||||||
};
|
const lunarInfo = this.baziAnalyzer.calculateLunarInfo(dateStr);
|
||||||
|
|
||||||
|
return {
|
||||||
|
year: lunarInfo.ganzhi_year,
|
||||||
|
month: lunarInfo.lunar_month,
|
||||||
|
day: lunarInfo.lunar_day,
|
||||||
|
description: lunarInfo.lunar_date,
|
||||||
|
zodiac: lunarInfo.zodiac,
|
||||||
|
solar_term: lunarInfo.solar_term
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
console.error('农历信息计算失败:', error);
|
||||||
|
// 降级处理:返回基本信息
|
||||||
|
return {
|
||||||
|
year: '未知',
|
||||||
|
month: '未知',
|
||||||
|
day: '未知',
|
||||||
|
description: '农历信息计算失败'
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 辅助方法:分类问题类型
|
// 辅助方法:分类问题类型
|
||||||
@@ -1928,14 +1990,265 @@ class QimenAnalyzer {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
generateDetailedAnalysis(yongShenAnalysis, patterns) {
|
/**
|
||||||
// 生成详细分析
|
* 生成详细分析
|
||||||
return ['用神状态良好', '格局组合有利'];
|
* @param {Object} qimenPan - 奇门盘
|
||||||
|
* @param {Object} yongShenAnalysis - 用神分析
|
||||||
|
* @param {Array} patterns - 格局列表
|
||||||
|
* @returns {Object} 详细分析
|
||||||
|
*/
|
||||||
|
generateDetailedAnalysis(qimenPan, yongShenAnalysis, patterns) {
|
||||||
|
const analysis = {
|
||||||
|
yongshen_status: this.generateYongShenStatusAnalysis(yongShenAnalysis),
|
||||||
|
pattern_influence: this.generatePatternInfluenceAnalysis(patterns),
|
||||||
|
palace_analysis: this.generatePalaceAnalysis(qimenPan),
|
||||||
|
wuxing_balance: this.generateWuXingAnalysis(qimenPan),
|
||||||
|
timing_factors: this.generateTimingAnalysis(qimenPan),
|
||||||
|
overall_trend: this.generateOverallTrendAnalysis(yongShenAnalysis, patterns)
|
||||||
|
};
|
||||||
|
|
||||||
|
return analysis;
|
||||||
}
|
}
|
||||||
|
|
||||||
generateSuggestions(yongShenAnalysis, qimenPan, question) {
|
/**
|
||||||
// 生成建议
|
* 生成用神状态分析
|
||||||
return ['把握时机', '积极行动'];
|
*/
|
||||||
|
generateYongShenStatusAnalysis(yongShenAnalysis) {
|
||||||
|
if (!yongShenAnalysis || !yongShenAnalysis.overall) {
|
||||||
|
return '用神分析数据不完整,无法进行详细评估';
|
||||||
|
}
|
||||||
|
|
||||||
|
const favorability = yongShenAnalysis.overall.favorability || 50;
|
||||||
|
|
||||||
|
if (favorability >= 80) {
|
||||||
|
return '用神状态极佳,各项要素配合得当,时机非常有利,建议积极行动';
|
||||||
|
} else if (favorability >= 65) {
|
||||||
|
return '用神状态良好,大部分要素较为有利,整体趋势向好,可以适度推进';
|
||||||
|
} else if (favorability >= 50) {
|
||||||
|
return '用神状态一般,有利不利因素并存,需要谨慎评估,稳步前进';
|
||||||
|
} else if (favorability >= 35) {
|
||||||
|
return '用神状态偏弱,不利因素较多,建议暂缓行动,等待更好时机';
|
||||||
|
} else {
|
||||||
|
return '用神状态不佳,阻力较大,不宜贸然行动,应重新规划策略';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成格局影响分析
|
||||||
|
*/
|
||||||
|
generatePatternInfluenceAnalysis(patterns) {
|
||||||
|
if (!patterns || patterns.length === 0) {
|
||||||
|
return '未发现明显格局,整体影响中性,需要综合其他因素判断';
|
||||||
|
}
|
||||||
|
|
||||||
|
const auspiciousCount = patterns.filter(p => p.level === '吉' || p.level === '大吉').length;
|
||||||
|
const inauspiciousCount = patterns.filter(p => p.level === '凶' || p.level === '大凶').length;
|
||||||
|
|
||||||
|
if (auspiciousCount > inauspiciousCount) {
|
||||||
|
return `发现${auspiciousCount}个吉利格局,${inauspiciousCount}个不利格局,整体格局偏向有利,可以借助吉格之力推进事情发展`;
|
||||||
|
} else if (inauspiciousCount > auspiciousCount) {
|
||||||
|
return `发现${inauspiciousCount}个不利格局,${auspiciousCount}个吉利格局,需要化解凶格影响,谨慎行事避免不利后果`;
|
||||||
|
} else {
|
||||||
|
return `吉凶格局数量相当,影响相互抵消,整体趋势平稳,需要依靠个人努力创造机会`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成宫位分析
|
||||||
|
*/
|
||||||
|
generatePalaceAnalysis(qimenPan) {
|
||||||
|
if (!qimenPan || !qimenPan.dipan) {
|
||||||
|
return '奇门盘数据不完整,无法进行宫位分析';
|
||||||
|
}
|
||||||
|
|
||||||
|
const palaceNames = ['坎宫', '坤宫', '震宫', '巽宫', '中宫', '乾宫', '兑宫', '艮宫', '离宫'];
|
||||||
|
const activePalaces = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < 9; i++) {
|
||||||
|
const palace = qimenPan.dipan[i];
|
||||||
|
if (palace && (palace.star || palace.door || palace.god)) {
|
||||||
|
activePalaces.push(palaceNames[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return `当前奇门盘中${activePalaces.join('、')}等宫位较为活跃,星门神配置完整,形成了相对稳定的能量分布格局`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成五行分析
|
||||||
|
*/
|
||||||
|
generateWuXingAnalysis(qimenPan) {
|
||||||
|
const wuxingCount = { '金': 0, '木': 0, '水': 0, '火': 0, '土': 0 };
|
||||||
|
|
||||||
|
if (qimenPan && qimenPan.dipan) {
|
||||||
|
for (let i = 0; i < 9; i++) {
|
||||||
|
const palace = qimenPan.dipan[i];
|
||||||
|
if (palace && palace.gan) {
|
||||||
|
const wuxing = this.getGanZhiWuXing(palace.gan);
|
||||||
|
if (wuxingCount[wuxing] !== undefined) {
|
||||||
|
wuxingCount[wuxing]++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const maxWuxing = Object.keys(wuxingCount).reduce((a, b) => wuxingCount[a] > wuxingCount[b] ? a : b);
|
||||||
|
const total = Object.values(wuxingCount).reduce((sum, count) => sum + count, 0);
|
||||||
|
|
||||||
|
if (total === 0) {
|
||||||
|
return { dominant: '未知', balance: '无法判断', suggestions: '数据不足,无法分析' };
|
||||||
|
}
|
||||||
|
|
||||||
|
const dominantRatio = wuxingCount[maxWuxing] / total;
|
||||||
|
let balance, suggestions;
|
||||||
|
|
||||||
|
if (dominantRatio >= 0.5) {
|
||||||
|
balance = '失衡';
|
||||||
|
suggestions = `${maxWuxing}过旺,需要其他五行调和平衡`;
|
||||||
|
} else if (dominantRatio >= 0.3) {
|
||||||
|
balance = '较为平衡';
|
||||||
|
suggestions = `${maxWuxing}稍强,整体尚可,注意维持平衡`;
|
||||||
|
} else {
|
||||||
|
balance = '平衡';
|
||||||
|
suggestions = '五行分布均匀,相互制约,整体和谐';
|
||||||
|
}
|
||||||
|
|
||||||
|
return { dominant: maxWuxing, balance, suggestions };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成时机分析
|
||||||
|
*/
|
||||||
|
generateTimingAnalysis(qimenPan) {
|
||||||
|
const currentDate = new Date();
|
||||||
|
const season = this.getCurrentSeason(currentDate);
|
||||||
|
|
||||||
|
let favorability, notes;
|
||||||
|
|
||||||
|
switch (season) {
|
||||||
|
case '春季':
|
||||||
|
favorability = '有利';
|
||||||
|
notes = '春季木旺,万物复苏,利于新的开始和发展';
|
||||||
|
break;
|
||||||
|
case '夏季':
|
||||||
|
favorability = '较为有利';
|
||||||
|
notes = '夏季火旺,阳气充沛,利于积极行动和扩展';
|
||||||
|
break;
|
||||||
|
case '秋季':
|
||||||
|
favorability = '中等';
|
||||||
|
notes = '秋季金旺,收获季节,利于总结和巩固成果';
|
||||||
|
break;
|
||||||
|
case '冬季':
|
||||||
|
favorability = '需谨慎';
|
||||||
|
notes = '冬季水旺,宜蛰伏养精,不宜大动作';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
favorability = '中等';
|
||||||
|
notes = '时机平常,需要综合其他因素判断';
|
||||||
|
}
|
||||||
|
|
||||||
|
return { season, favorability, notes };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成整体趋势分析
|
||||||
|
*/
|
||||||
|
generateOverallTrendAnalysis(yongShenAnalysis, patterns) {
|
||||||
|
const favorability = yongShenAnalysis?.overall?.favorability || 50;
|
||||||
|
const patternScore = patterns ? patterns.reduce((sum, p) => sum + (p.score || 0), 0) : 0;
|
||||||
|
|
||||||
|
const totalScore = favorability + patternScore;
|
||||||
|
|
||||||
|
if (totalScore >= 80) {
|
||||||
|
return '整体趋势非常积极,各项因素配合良好,成功概率很高,建议抓住机会全力推进';
|
||||||
|
} else if (totalScore >= 60) {
|
||||||
|
return '整体趋势较为积极,大部分因素有利,有较好的成功基础,可以稳步推进';
|
||||||
|
} else if (totalScore >= 40) {
|
||||||
|
return '整体趋势中性偏好,有利不利因素并存,需要谨慎规划,稳中求进';
|
||||||
|
} else if (totalScore >= 20) {
|
||||||
|
return '整体趋势偏向不利,阻力较大,建议暂缓行动,寻找更好时机';
|
||||||
|
} else {
|
||||||
|
return '整体趋势不佳,不利因素较多,不建议贸然行动,应重新评估策略';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前季节
|
||||||
|
*/
|
||||||
|
getCurrentSeason(date) {
|
||||||
|
const month = date.getMonth() + 1;
|
||||||
|
|
||||||
|
if (month >= 3 && month <= 5) {
|
||||||
|
return '春季';
|
||||||
|
} else if (month >= 6 && month <= 8) {
|
||||||
|
return '夏季';
|
||||||
|
} else if (month >= 9 && month <= 11) {
|
||||||
|
return '秋季';
|
||||||
|
} else {
|
||||||
|
return '冬季';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成建议
|
||||||
|
* @param {Object} yongShenAnalysis - 用神分析
|
||||||
|
* @param {Array} patterns - 格局列表
|
||||||
|
* @param {Object} timing - 应期分析
|
||||||
|
* @returns {Array} 建议列表
|
||||||
|
*/
|
||||||
|
generateSuggestions(yongShenAnalysis, patterns, timing) {
|
||||||
|
const suggestions = [];
|
||||||
|
|
||||||
|
// 基于用神状态的建议
|
||||||
|
if (yongShenAnalysis && yongShenAnalysis.overall) {
|
||||||
|
const favorability = yongShenAnalysis.overall.favorability || 50;
|
||||||
|
|
||||||
|
if (favorability >= 70) {
|
||||||
|
suggestions.push('用神得力,时机有利,可以积极主动地推进计划');
|
||||||
|
suggestions.push('抓住当前的有利时机,果断行动,成功概率较高');
|
||||||
|
} else if (favorability >= 50) {
|
||||||
|
suggestions.push('用神状态一般,需要谨慎评估,稳步推进');
|
||||||
|
suggestions.push('可以适度行动,但要做好充分准备和风险控制');
|
||||||
|
} else {
|
||||||
|
suggestions.push('用神不利,建议暂缓行动,等待更好的时机');
|
||||||
|
suggestions.push('当前阻力较大,宜以守为攻,积蓄力量');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 基于格局的建议
|
||||||
|
if (patterns && patterns.length > 0) {
|
||||||
|
const auspiciousPatterns = patterns.filter(p => p.level === '吉' || p.level === '大吉');
|
||||||
|
const inauspiciousPatterns = patterns.filter(p => p.level === '凶' || p.level === '大凶');
|
||||||
|
|
||||||
|
if (auspiciousPatterns.length > inauspiciousPatterns.length) {
|
||||||
|
suggestions.push('格局组合整体有利,可以借助贵人力量,寻求合作机会');
|
||||||
|
suggestions.push('吉格当头,宜主动出击,把握机遇,扩大成果');
|
||||||
|
} else if (inauspiciousPatterns.length > 0) {
|
||||||
|
suggestions.push('存在不利格局,需要化解阻碍,避免冲动行事');
|
||||||
|
suggestions.push('凶格影响,宜低调行事,避免锋芒太露,以免招致麻烦');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 基于应期的建议
|
||||||
|
if (timing && timing.mainTiming) {
|
||||||
|
switch (timing.mainTiming) {
|
||||||
|
case '近期':
|
||||||
|
suggestions.push('应期在即,宜抓紧时间行动,不可错失良机');
|
||||||
|
break;
|
||||||
|
case '中期':
|
||||||
|
suggestions.push('需要耐心等待,做好充分准备,时机成熟时再行动');
|
||||||
|
break;
|
||||||
|
case '远期':
|
||||||
|
suggestions.push('应期较远,宜长远规划,循序渐进,不可急于求成');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 通用建议
|
||||||
|
suggestions.push('保持积极心态,相信自己的判断,同时要灵活应变');
|
||||||
|
suggestions.push('多与有经验的人交流,听取不同意见,完善行动方案');
|
||||||
|
|
||||||
|
return suggestions;
|
||||||
}
|
}
|
||||||
|
|
||||||
calculateTiming(yongShenAnalysis, qimenPan) {
|
calculateTiming(yongShenAnalysis, qimenPan) {
|
||||||
@@ -2871,7 +3184,7 @@ class YongShenAnalyzer {
|
|||||||
* @returns {Object} 用神配置
|
* @returns {Object} 用神配置
|
||||||
*/
|
*/
|
||||||
selectYongShen(question, birthData, qimenPan) {
|
selectYongShen(question, birthData, qimenPan) {
|
||||||
const questionType = this.classifyQuestion(question);
|
const questionType = this.analyzer.classifyQuestion(question);
|
||||||
const rigan = qimenPan.timeInfo.day.gan;
|
const rigan = qimenPan.timeInfo.day.gan;
|
||||||
const gender = birthData?.gender || '未知';
|
const gender = birthData?.gender || '未知';
|
||||||
|
|
||||||
@@ -3858,8 +4171,8 @@ class YongShenAnalyzer {
|
|||||||
recommendation,
|
recommendation,
|
||||||
factors,
|
factors,
|
||||||
analysis: {
|
analysis: {
|
||||||
zhifuAnalysis: zhifu ? `值符${zhifu.star}` : '值符未知',
|
zhifuAnalysis: zhifu ? `值符${typeof zhifu === 'object' ? zhifu.star : zhifu}` : '值符未知',
|
||||||
zhishiAnalysis: zhishi ? `值使${zhishi.door}` : '值使未知',
|
zhishiAnalysis: zhishi ? `值使${typeof zhishi === 'object' ? zhishi.door : zhishi}` : '值使未知',
|
||||||
hourAnalysis: `${hourZhi}时`,
|
hourAnalysis: `${hourZhi}时`,
|
||||||
seasonAnalysis: `${jieqi}节气`,
|
seasonAnalysis: `${jieqi}节气`,
|
||||||
yindunAnalysis: timeInfo.yindun ? '阴遁' : '阳遁'
|
yindunAnalysis: timeInfo.yindun ? '阴遁' : '阳遁'
|
||||||
@@ -3887,13 +4200,14 @@ class PredictionGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
generatePrediction(qimenPan, yongShenAnalysis, question, patterns) {
|
generatePrediction(qimenPan, yongShenAnalysis, question, patterns) {
|
||||||
const score = this.calculateOverallScore(yongShenAnalysis, patterns);
|
// 使用主分析器的概率计算方法
|
||||||
|
const probability = this.analyzer.calculateProbability(yongShenAnalysis, patterns);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
overall: this.interpretScore(score),
|
overall: this.analyzer.generateOverallAssessment(probability, yongShenAnalysis),
|
||||||
probability: score,
|
probability: probability,
|
||||||
details: this.generateDetailedAnalysis(yongShenAnalysis, patterns),
|
details: this.analyzer.generateDetailedAnalysis(qimenPan, yongShenAnalysis, patterns),
|
||||||
suggestions: this.generateSuggestions(yongShenAnalysis, qimenPan, question),
|
suggestions: this.analyzer.generateSuggestions(yongShenAnalysis, patterns, { description: '时机分析' }),
|
||||||
timing: this.calculateTiming(yongShenAnalysis, qimenPan)
|
timing: this.calculateTiming(yongShenAnalysis, qimenPan)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
// 专业紫微斗数分析服务模块
|
// 专业紫微斗数分析服务模块
|
||||||
// 基于传统紫微斗数理论的精确实现
|
// 基于传统紫微斗数理论的精确实现
|
||||||
|
|
||||||
const BaziAnalyzer = require('./baziAnalyzer.cjs');
|
|
||||||
const BaseData = require('./common/BaseData.cjs');
|
const BaseData = require('./common/BaseData.cjs');
|
||||||
const AnalysisCache = require('./common/AnalysisCache.cjs');
|
const AnalysisCache = require('./common/AnalysisCache.cjs');
|
||||||
const StarBrightness = require('./common/StarBrightness.cjs');
|
const StarBrightness = require('./common/StarBrightness.cjs');
|
||||||
const EnhancedSiHua = require('./common/EnhancedSiHua.cjs');
|
const EnhancedSiHua = require('./common/EnhancedSiHua.cjs');
|
||||||
|
const BaziAnalyzer = require('./baziAnalyzer.cjs');
|
||||||
|
|
||||||
class ZiweiAnalyzer {
|
class ZiweiAnalyzer {
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -61,42 +61,27 @@ class ZiweiAnalyzer {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算农历信息(与八字分析器保持一致)
|
// 计算农历信息(复用八字分析器的农历算法)
|
||||||
calculateLunarInfo(birth_date) {
|
calculateLunarInfo(birth_date) {
|
||||||
const birthDate = new Date(birth_date);
|
try {
|
||||||
const year = birthDate.getFullYear();
|
// 直接复用八字分析器的农历计算功能,避免重复实现
|
||||||
const month = birthDate.getMonth() + 1;
|
return this.baziAnalyzer.calculateLunarInfo(birth_date);
|
||||||
const day = birthDate.getDate();
|
} catch (error) {
|
||||||
|
console.error('农历信息计算失败:', error);
|
||||||
// 计算干支年
|
// 降级处理:返回基本信息
|
||||||
const tianGan = ['甲', '乙', '丙', '丁', '戊', '己', '庚', '辛', '壬', '癸'];
|
const birthDate = new Date(birth_date);
|
||||||
const diZhi = ['子', '丑', '寅', '卯', '辰', '巳', '午', '未', '申', '酉', '戌', '亥'];
|
const year = birthDate.getFullYear();
|
||||||
const zodiacAnimals = ['鼠', '牛', '虎', '兔', '龙', '蛇', '马', '羊', '猴', '鸡', '狗', '猪'];
|
return {
|
||||||
|
lunar_date: '农历信息计算失败',
|
||||||
const ganIndex = (year - 4) % 10;
|
lunar_year: `${year}年`,
|
||||||
const zhiIndex = (year - 4) % 12;
|
lunar_month: '未知月',
|
||||||
const ganzhiYear = tianGan[ganIndex] + diZhi[zhiIndex];
|
lunar_day: '未知日',
|
||||||
const zodiac = zodiacAnimals[zhiIndex];
|
ganzhi_year: '未知',
|
||||||
|
zodiac: '未知',
|
||||||
// 计算节气信息
|
solar_term: '未知'
|
||||||
let solarTerm = this.calculateSolarTerm(month, day);
|
|
||||||
|
|
||||||
// 改进的农历日期计算
|
|
||||||
const lunarInfo = this.calculateAccurateLunarDate(year, month, day);
|
|
||||||
const lunarDay = lunarInfo.day;
|
|
||||||
const lunarMonth = lunarInfo.month;
|
|
||||||
const lunarYear = lunarInfo.year;
|
|
||||||
|
|
||||||
return {
|
|
||||||
lunar_date: `农历${this.getChineseYear(lunarYear)}年${this.getChineseMonth(lunarMonth)}月${this.getChineseDay(lunarDay)}日`,
|
|
||||||
lunar_year: `${this.getChineseYear(lunarYear)}年`,
|
|
||||||
lunar_month: this.getChineseMonth(lunarMonth) + '月',
|
|
||||||
lunar_day: this.getChineseDay(lunarDay) + '日',
|
|
||||||
ganzhi_year: ganzhiYear,
|
|
||||||
zodiac: zodiac,
|
|
||||||
solar_term: this.calculateDetailedSolarTerm(month, day)
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 转换为中文年份
|
// 转换为中文年份
|
||||||
getChineseYear(year) {
|
getChineseYear(year) {
|
||||||
@@ -152,81 +137,7 @@ class ZiweiAnalyzer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 改进的公历转农历计算方法(与八字分析器保持一致)
|
// 已删除重复的农历计算方法,现在复用八字分析器的功能
|
||||||
calculateAccurateLunarDate(year, month, day) {
|
|
||||||
// 农历年份对照表(部分年份的春节日期)
|
|
||||||
const springFestivals = {
|
|
||||||
1976: { month: 1, day: 31 }, // 1976年春节:1月31日
|
|
||||||
1977: { month: 2, day: 18 },
|
|
||||||
1978: { month: 2, day: 7 },
|
|
||||||
1979: { month: 1, day: 28 },
|
|
||||||
1980: { month: 2, day: 16 },
|
|
||||||
1981: { month: 2, day: 5 },
|
|
||||||
1982: { month: 1, day: 25 },
|
|
||||||
1983: { month: 2, day: 13 },
|
|
||||||
1984: { month: 2, day: 2 },
|
|
||||||
1985: { month: 2, day: 20 },
|
|
||||||
1986: { month: 2, day: 9 },
|
|
||||||
1987: { month: 1, day: 29 },
|
|
||||||
1988: { month: 2, day: 17 },
|
|
||||||
1989: { month: 2, day: 6 },
|
|
||||||
1990: { month: 1, day: 27 }
|
|
||||||
};
|
|
||||||
|
|
||||||
const springFestival = springFestivals[year];
|
|
||||||
if (!springFestival) {
|
|
||||||
// 如果没有对应年份数据,使用估算
|
|
||||||
return {
|
|
||||||
year: year,
|
|
||||||
month: month > 2 ? month - 1 : month + 11,
|
|
||||||
day: Math.max(1, day - 15)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// 计算距离春节的天数
|
|
||||||
const currentDate = new Date(year, month - 1, day);
|
|
||||||
const springDate = new Date(year, springFestival.month - 1, springFestival.day);
|
|
||||||
const daysDiff = Math.floor((currentDate - springDate) / (1000 * 60 * 60 * 24));
|
|
||||||
|
|
||||||
if (daysDiff < 0) {
|
|
||||||
// 在春节之前,属于上一年农历
|
|
||||||
const prevSpringFestival = springFestivals[year - 1];
|
|
||||||
if (prevSpringFestival) {
|
|
||||||
const prevSpringDate = new Date(year - 1, prevSpringFestival.month - 1, prevSpringFestival.day);
|
|
||||||
const prevDaysDiff = Math.floor((currentDate - prevSpringDate) / (1000 * 60 * 60 * 24));
|
|
||||||
const totalDays = prevDaysDiff + 365; // 简化计算
|
|
||||||
|
|
||||||
// 估算农历月日
|
|
||||||
const lunarMonth = Math.floor(totalDays / 30) + 1;
|
|
||||||
const lunarDay = (totalDays % 30) + 1;
|
|
||||||
|
|
||||||
return {
|
|
||||||
year: year - 1,
|
|
||||||
month: Math.min(12, lunarMonth),
|
|
||||||
day: Math.min(30, lunarDay)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 在春节之后,计算农历月日
|
|
||||||
const lunarMonth = Math.floor(daysDiff / 30) + 1;
|
|
||||||
const lunarDay = (daysDiff % 30) + 1;
|
|
||||||
|
|
||||||
// 特殊处理:1976年3月17日应该对应农历2月17日左右
|
|
||||||
if (year === 1976 && month === 3 && day === 17) {
|
|
||||||
return {
|
|
||||||
year: 1976,
|
|
||||||
month: 2,
|
|
||||||
day: 17
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
year: year,
|
|
||||||
month: Math.min(12, lunarMonth),
|
|
||||||
day: Math.min(30, Math.max(1, lunarDay))
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// 计算节气信息
|
// 计算节气信息
|
||||||
calculateSolarTerm(month, day) {
|
calculateSolarTerm(month, day) {
|
||||||
@@ -257,19 +168,7 @@ class ZiweiAnalyzer {
|
|||||||
return '节气间';
|
return '节气间';
|
||||||
}
|
}
|
||||||
|
|
||||||
// 转换为中文月份
|
// 已删除重复的中文转换方法,现在复用八字分析器的功能
|
||||||
getChineseMonth(month) {
|
|
||||||
const chineseMonths = ['', '正', '二', '三', '四', '五', '六', '七', '八', '九', '十', '十一', '腊'];
|
|
||||||
return chineseMonths[month] || '未知';
|
|
||||||
}
|
|
||||||
|
|
||||||
// 转换为中文日期
|
|
||||||
getChineseDay(day) {
|
|
||||||
const chineseDays = ['', '初一', '初二', '初三', '初四', '初五', '初六', '初七', '初八', '初九', '初十',
|
|
||||||
'十一', '十二', '十三', '十四', '十五', '十六', '十七', '十八', '十九', '二十',
|
|
||||||
'廿一', '廿二', '廿三', '廿四', '廿五', '廿六', '廿七', '廿八', '廿九', '三十'];
|
|
||||||
return chineseDays[day] || '未知';
|
|
||||||
}
|
|
||||||
|
|
||||||
// 生成子时计算方法说明(紫微斗数版本)
|
// 生成子时计算方法说明(紫微斗数版本)
|
||||||
generateZishiCalculationNote(baziInfo, birth_time) {
|
generateZishiCalculationNote(baziInfo, birth_time) {
|
||||||
|
|||||||
@@ -527,7 +527,7 @@ class SolarTerms {
|
|||||||
* @returns {Date} 立春时间
|
* @returns {Date} 立春时间
|
||||||
*/
|
*/
|
||||||
getSpringBeginning(year) {
|
getSpringBeginning(year) {
|
||||||
return this.calculateSolarTerm(year, 0);
|
return this.calculateSolarTermDate(year, '立春');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -2,11 +2,12 @@ import React from 'react';
|
|||||||
import CompleteBaziAnalysis from './CompleteBaziAnalysis';
|
import CompleteBaziAnalysis from './CompleteBaziAnalysis';
|
||||||
import CompleteZiweiAnalysis from './CompleteZiweiAnalysis';
|
import CompleteZiweiAnalysis from './CompleteZiweiAnalysis';
|
||||||
import CompleteYijingAnalysis from './CompleteYijingAnalysis';
|
import CompleteYijingAnalysis from './CompleteYijingAnalysis';
|
||||||
|
import CompleteQimenAnalysis from './CompleteQimenAnalysis';
|
||||||
import BaziAnalysisDisplay from './BaziAnalysisDisplay';
|
import BaziAnalysisDisplay from './BaziAnalysisDisplay';
|
||||||
|
|
||||||
interface AnalysisResultDisplayProps {
|
interface AnalysisResultDisplayProps {
|
||||||
analysisResult?: any;
|
analysisResult?: any;
|
||||||
analysisType: 'bazi' | 'ziwei' | 'yijing';
|
analysisType: 'bazi' | 'ziwei' | 'yijing' | 'qimen';
|
||||||
birthDate?: {
|
birthDate?: {
|
||||||
date: string;
|
date: string;
|
||||||
time: string;
|
time: string;
|
||||||
@@ -429,6 +430,8 @@ const AnalysisResultDisplay: React.FC<AnalysisResultDisplayProps> = ({
|
|||||||
return renderZiweiAnalysis();
|
return renderZiweiAnalysis();
|
||||||
case 'yijing':
|
case 'yijing':
|
||||||
return renderYijingAnalysis();
|
return renderYijingAnalysis();
|
||||||
|
case 'qimen':
|
||||||
|
return <CompleteQimenAnalysis analysis={analysisResult?.data || analysisResult} />;
|
||||||
default:
|
default:
|
||||||
return (
|
return (
|
||||||
<div className="bg-white rounded-lg p-6 shadow-lg">
|
<div className="bg-white rounded-lg p-6 shadow-lg">
|
||||||
@@ -461,6 +464,11 @@ const AnalysisResultDisplay: React.FC<AnalysisResultDisplayProps> = ({
|
|||||||
return <CompleteZiweiAnalysis birthDate={birthDate} analysisData={preAnalysisData} recordId={recordId} />;
|
return <CompleteZiweiAnalysis birthDate={birthDate} analysisData={preAnalysisData} recordId={recordId} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 对于奇门遁甲,如果有预分析数据,直接返回 CompleteQimenAnalysis 组件
|
||||||
|
if (analysisType === 'qimen' && preAnalysisData) {
|
||||||
|
return <CompleteQimenAnalysis analysis={preAnalysisData} recordId={recordId} />;
|
||||||
|
}
|
||||||
|
|
||||||
// 如果没有分析结果数据
|
// 如果没有分析结果数据
|
||||||
if (!analysisResult) {
|
if (!analysisResult) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
1457
src/components/CompleteQimenAnalysis.tsx
Normal file
1457
src/components/CompleteQimenAnalysis.tsx
Normal file
File diff suppressed because it is too large
Load Diff
@@ -11,7 +11,7 @@ export type ExportMode = 'server' | 'frontend';
|
|||||||
|
|
||||||
interface DownloadButtonProps {
|
interface DownloadButtonProps {
|
||||||
analysisData: any;
|
analysisData: any;
|
||||||
analysisType: 'bazi' | 'ziwei' | 'yijing';
|
analysisType: 'bazi' | 'ziwei' | 'yijing' | 'qimen';
|
||||||
userName?: string;
|
userName?: string;
|
||||||
onDownload?: (format: DownloadFormat) => Promise<void>;
|
onDownload?: (format: DownloadFormat) => Promise<void>;
|
||||||
className?: string;
|
className?: string;
|
||||||
@@ -480,6 +480,7 @@ const DownloadButton: React.FC<DownloadButtonProps> = ({
|
|||||||
case 'bazi': return '八字命理';
|
case 'bazi': return '八字命理';
|
||||||
case 'ziwei': return '紫微斗数';
|
case 'ziwei': return '紫微斗数';
|
||||||
case 'yijing': return '易经占卜';
|
case 'yijing': return '易经占卜';
|
||||||
|
case 'qimen': return '奇门遁甲';
|
||||||
default: return '命理';
|
default: return '命理';
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ interface YijingQuestionSelectorProps {
|
|||||||
value: string;
|
value: string;
|
||||||
onChange: (value: string) => void;
|
onChange: (value: string) => void;
|
||||||
className?: string;
|
className?: string;
|
||||||
|
label?: string;
|
||||||
|
placeholder?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 问题分类和预设问题数据
|
// 问题分类和预设问题数据
|
||||||
@@ -115,7 +117,9 @@ const questionCategories = {
|
|||||||
export const YijingQuestionSelector: React.FC<YijingQuestionSelectorProps> = ({
|
export const YijingQuestionSelector: React.FC<YijingQuestionSelectorProps> = ({
|
||||||
value,
|
value,
|
||||||
onChange,
|
onChange,
|
||||||
className
|
className,
|
||||||
|
label = '占卜问题',
|
||||||
|
placeholder = '请输入您要占卜的问题'
|
||||||
}) => {
|
}) => {
|
||||||
const [selectedCategory, setSelectedCategory] = useState<string>('');
|
const [selectedCategory, setSelectedCategory] = useState<string>('');
|
||||||
const [selectedQuestion, setSelectedQuestion] = useState<string>('');
|
const [selectedQuestion, setSelectedQuestion] = useState<string>('');
|
||||||
@@ -261,10 +265,10 @@ variant="default"
|
|||||||
|
|
||||||
{/* 主要问题输入框 */}
|
{/* 主要问题输入框 */}
|
||||||
<ChineseInput
|
<ChineseInput
|
||||||
label="占卜问题"
|
label={label}
|
||||||
value={value}
|
value={value}
|
||||||
onChange={(e) => onChange(e.target.value)}
|
onChange={(e) => onChange(e.target.value)}
|
||||||
placeholder="请输入您希望占卜的具体问题,或选择上方预设问题"
|
placeholder={placeholder}
|
||||||
required
|
required
|
||||||
variant="filled"
|
variant="filled"
|
||||||
helperText="💡 提示:问题越具体,占卜结果越准确。您可以使用预设问题或自行输入。"
|
helperText="💡 提示:问题越具体,占卜结果越准确。您可以使用预设问题或自行输入。"
|
||||||
|
|||||||
@@ -68,7 +68,24 @@ export const aiPromptTemplates = {
|
|||||||
易经占卜结果:
|
易经占卜结果:
|
||||||
{analysisContent}
|
{analysisContent}
|
||||||
|
|
||||||
请提供智慧的AI解读:`
|
请提供智慧的AI解读:`,
|
||||||
|
|
||||||
|
qimen: `你是一位精通奇门遁甲的预测大师,请对以下奇门遁甲分析结果进行专业解读。
|
||||||
|
|
||||||
|
请重点分析:
|
||||||
|
1. 奇门盘局的整体格局特点
|
||||||
|
2. 用神落宫的吉凶分析
|
||||||
|
3. 九星八门八神的组合意义
|
||||||
|
4. 格局对事情发展的具体影响
|
||||||
|
5. 最佳行动时机和策略建议
|
||||||
|
6. 需要注意的不利因素
|
||||||
|
|
||||||
|
请结合现代实际情况,提供具有指导价值的预测分析。
|
||||||
|
|
||||||
|
奇门遁甲分析结果:
|
||||||
|
{analysisContent}
|
||||||
|
|
||||||
|
请提供专业的AI解读:`
|
||||||
};
|
};
|
||||||
|
|
||||||
// 获取AI配置
|
// 获取AI配置
|
||||||
@@ -103,6 +120,6 @@ export const validateAIConfig = (config: AIConfig): boolean => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 获取提示词模板
|
// 获取提示词模板
|
||||||
export const getPromptTemplate = (analysisType: 'bazi' | 'ziwei' | 'yijing'): string => {
|
export const getPromptTemplate = (analysisType: 'bazi' | 'ziwei' | 'yijing' | 'qimen'): string => {
|
||||||
return aiPromptTemplates[analysisType] || aiPromptTemplates.bazi;
|
return aiPromptTemplates[analysisType] || aiPromptTemplates.bazi;
|
||||||
};
|
};
|
||||||
@@ -264,6 +264,14 @@ class LocalApiClient {
|
|||||||
}, yijingData);
|
}, yijingData);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// 奇门遁甲分析
|
||||||
|
qimen: async (qimenData: any): Promise<ApiResponse<{ record_id: number; analysis: any }>> => {
|
||||||
|
return this.requestWithDeduplication<{ record_id: number; analysis: any }>('/qimen/analyze', {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify(qimenData),
|
||||||
|
}, qimenData);
|
||||||
|
},
|
||||||
|
|
||||||
// 综合分析
|
// 综合分析
|
||||||
comprehensive: async (birthData: any, includeTypes?: string[]): Promise<ApiResponse<{ record_id: number; analysis: any }>> => {
|
comprehensive: async (birthData: any, includeTypes?: string[]): Promise<ApiResponse<{ record_id: number; analysis: any }>> => {
|
||||||
return this.request<{ record_id: number; analysis: any }>('/analysis/comprehensive', {
|
return this.request<{ record_id: number; analysis: any }>('/analysis/comprehensive', {
|
||||||
|
|||||||
@@ -8,11 +8,11 @@ import { ChineseCard, ChineseCardContent, ChineseCardHeader, ChineseCardTitle }
|
|||||||
import YijingQuestionSelector from '../components/ui/YijingQuestionSelector';
|
import YijingQuestionSelector from '../components/ui/YijingQuestionSelector';
|
||||||
import AnalysisResultDisplay from '../components/AnalysisResultDisplay';
|
import AnalysisResultDisplay from '../components/AnalysisResultDisplay';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { Sparkles, Star, Compass, Calendar, MapPin, User, Loader2 } from 'lucide-react';
|
import { Sparkles, Star, Compass, Calendar, MapPin, User, Loader2, Hexagon } from 'lucide-react';
|
||||||
import { UserProfile, AnalysisRequest, NumerologyReading } from '../types';
|
import { UserProfile, AnalysisRequest, NumerologyReading } from '../types';
|
||||||
import { cn } from '../lib/utils';
|
import { cn } from '../lib/utils';
|
||||||
|
|
||||||
type AnalysisType = 'bazi' | 'ziwei' | 'yijing';
|
type AnalysisType = 'bazi' | 'ziwei' | 'yijing' | 'qimen';
|
||||||
|
|
||||||
const AnalysisPage: React.FC = () => {
|
const AnalysisPage: React.FC = () => {
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
@@ -32,7 +32,7 @@ const AnalysisPage: React.FC = () => {
|
|||||||
|
|
||||||
// 使用useMemo缓存birthDate对象,避免重复渲染导致useEffect重复执行
|
// 使用useMemo缓存birthDate对象,避免重复渲染导致useEffect重复执行
|
||||||
const memoizedBirthDate = useMemo(() => {
|
const memoizedBirthDate = useMemo(() => {
|
||||||
if (analysisType === 'bazi' || analysisType === 'ziwei') {
|
if (analysisType === 'bazi' || analysisType === 'ziwei' || analysisType === 'qimen') {
|
||||||
return {
|
return {
|
||||||
date: formData.birth_date,
|
date: formData.birth_date,
|
||||||
time: formData.birth_time,
|
time: formData.birth_time,
|
||||||
@@ -83,6 +83,15 @@ const AnalysisPage: React.FC = () => {
|
|||||||
toast.error('请填写占卜问题');
|
toast.error('请填写占卜问题');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
} else if (analysisType === 'qimen') {
|
||||||
|
if (!formData.question) {
|
||||||
|
toast.error('请填写占卜问题');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!formData.birth_date || !formData.birth_time) {
|
||||||
|
toast.error('奇门遁甲需要准确的出生日期和时间');
|
||||||
|
return;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!formData.name || !formData.birth_date) {
|
if (!formData.name || !formData.birth_date) {
|
||||||
toast.error('请填写姓名和出生日期');
|
toast.error('请填写姓名和出生日期');
|
||||||
@@ -123,6 +132,16 @@ const AnalysisPage: React.FC = () => {
|
|||||||
response = await localApi.analysis.yijing(yijingData);
|
response = await localApi.analysis.yijing(yijingData);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 'qimen': {
|
||||||
|
const qimenData = {
|
||||||
|
...birthData,
|
||||||
|
question: formData.question,
|
||||||
|
user_timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
||||||
|
local_time: new Date().toISOString()
|
||||||
|
};
|
||||||
|
response = await localApi.analysis.qimen(qimenData);
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
throw new Error(`不支持的分析类型: ${analysisType}`);
|
throw new Error(`不支持的分析类型: ${analysisType}`);
|
||||||
}
|
}
|
||||||
@@ -215,6 +234,15 @@ const AnalysisPage: React.FC = () => {
|
|||||||
color: 'text-orange-600',
|
color: 'text-orange-600',
|
||||||
bgColor: 'bg-orange-50',
|
bgColor: 'bg-orange-50',
|
||||||
borderColor: 'border-orange-300'
|
borderColor: 'border-orange-300'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'qimen' as AnalysisType,
|
||||||
|
title: '奇门遁甲',
|
||||||
|
description: '古代帝王之学,通过时空奇门盘分析事物发展趋势',
|
||||||
|
icon: Hexagon,
|
||||||
|
color: 'text-purple-600',
|
||||||
|
bgColor: 'bg-purple-50',
|
||||||
|
borderColor: 'border-purple-300'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -233,7 +261,7 @@ const AnalysisPage: React.FC = () => {
|
|||||||
<p className="text-gray-600 font-chinese">选择您感兴趣的命理分析方式</p>
|
<p className="text-gray-600 font-chinese">选择您感兴趣的命理分析方式</p>
|
||||||
</ChineseCardHeader>
|
</ChineseCardHeader>
|
||||||
<ChineseCardContent>
|
<ChineseCardContent>
|
||||||
<div className="grid sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
<div className="grid sm:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||||
{analysisTypes.map((type) => {
|
{analysisTypes.map((type) => {
|
||||||
const Icon = type.icon;
|
const Icon = type.icon;
|
||||||
const isSelected = analysisType === type.type;
|
const isSelected = analysisType === type.type;
|
||||||
@@ -291,6 +319,52 @@ const AnalysisPage: React.FC = () => {
|
|||||||
onChange={(value) => setFormData(prev => ({ ...prev, question: value }))}
|
onChange={(value) => setFormData(prev => ({ ...prev, question: value }))}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
) : analysisType === 'qimen' ? (
|
||||||
|
// 奇门遁甲表单
|
||||||
|
<>
|
||||||
|
<div className="mb-6">
|
||||||
|
<YijingQuestionSelector
|
||||||
|
value={formData.question}
|
||||||
|
onChange={(value) => setFormData(prev => ({ ...prev, question: value }))}
|
||||||
|
placeholder="请输入您要占卜的问题,如:事业发展、投资决策、感情婚姻等"
|
||||||
|
label="占卜问题"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid md:grid-cols-2 gap-4 md:gap-6 mb-6">
|
||||||
|
<div className="relative">
|
||||||
|
<ChineseInput
|
||||||
|
type="date"
|
||||||
|
label="出生日期"
|
||||||
|
value={formData.birth_date}
|
||||||
|
onChange={(e) => {
|
||||||
|
const value = e.target.value;
|
||||||
|
if (value && !/^\d{4}-\d{2}-\d{2}$/.test(value)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setFormData(prev => ({ ...prev, birth_date: value }));
|
||||||
|
}}
|
||||||
|
min="1900-01-01"
|
||||||
|
max="2100-12-31"
|
||||||
|
required
|
||||||
|
variant="filled"
|
||||||
|
className="pr-10"
|
||||||
|
helperText="奇门遁甲需要准确的出生日期"
|
||||||
|
/>
|
||||||
|
<Calendar className="absolute right-3 top-9 h-4 w-4 text-gray-400 pointer-events-none" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ChineseInput
|
||||||
|
type="time"
|
||||||
|
label="出生时间"
|
||||||
|
value={formData.birth_time}
|
||||||
|
onChange={(e) => setFormData(prev => ({ ...prev, birth_time: e.target.value }))}
|
||||||
|
required
|
||||||
|
variant="filled"
|
||||||
|
helperText="奇门遁甲必须填写准确的出生时间"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
) : (
|
) : (
|
||||||
// 八字和紫微表单
|
// 八字和紫微表单
|
||||||
<>
|
<>
|
||||||
@@ -402,9 +476,10 @@ const AnalysisPage: React.FC = () => {
|
|||||||
analysisResult={analysisResult}
|
analysisResult={analysisResult}
|
||||||
analysisType={analysisType}
|
analysisType={analysisType}
|
||||||
birthDate={memoizedBirthDate}
|
birthDate={memoizedBirthDate}
|
||||||
question={analysisType === 'yijing' ? formData.question : undefined}
|
question={analysisType === 'yijing' || analysisType === 'qimen' ? formData.question : undefined}
|
||||||
userId={user?.id?.toString()}
|
userId={user?.id?.toString()}
|
||||||
divinationMethod="time"
|
divinationMethod="time"
|
||||||
|
preAnalysisData={analysisResult.data}
|
||||||
recordId={analysisResult.recordId}
|
recordId={analysisResult.recordId}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { ChineseLoading } from '../components/ui/ChineseLoading';
|
|||||||
import AnalysisResultDisplay from '../components/AnalysisResultDisplay';
|
import AnalysisResultDisplay from '../components/AnalysisResultDisplay';
|
||||||
import DownloadButton from '../components/ui/DownloadButton';
|
import DownloadButton from '../components/ui/DownloadButton';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { History, Calendar, User, Sparkles, Star, Compass, Eye, Trash2, Download, ChevronLeft, ChevronRight } from 'lucide-react';
|
import { History, Calendar, User, Sparkles, Star, Compass, Hexagon, Eye, Trash2, Download, ChevronLeft, ChevronRight } from 'lucide-react';
|
||||||
import { NumerologyReading } from '../types';
|
import { NumerologyReading } from '../types';
|
||||||
import { cn } from '../lib/utils';
|
import { cn } from '../lib/utils';
|
||||||
|
|
||||||
@@ -145,6 +145,7 @@ const HistoryPage: React.FC = () => {
|
|||||||
case 'bazi': return Sparkles;
|
case 'bazi': return Sparkles;
|
||||||
case 'ziwei': return Star;
|
case 'ziwei': return Star;
|
||||||
case 'yijing': return Compass;
|
case 'yijing': return Compass;
|
||||||
|
case 'qimen': return Hexagon;
|
||||||
default: return History;
|
default: return History;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -154,6 +155,7 @@ const HistoryPage: React.FC = () => {
|
|||||||
case 'bazi': return 'text-red-600 bg-red-50';
|
case 'bazi': return 'text-red-600 bg-red-50';
|
||||||
case 'ziwei': return 'text-yellow-600 bg-yellow-50';
|
case 'ziwei': return 'text-yellow-600 bg-yellow-50';
|
||||||
case 'yijing': return 'text-orange-600 bg-orange-50';
|
case 'yijing': return 'text-orange-600 bg-orange-50';
|
||||||
|
case 'qimen': return 'text-purple-600 bg-purple-50';
|
||||||
default: return 'text-gray-600 bg-gray-50';
|
default: return 'text-gray-600 bg-gray-50';
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -163,6 +165,7 @@ const HistoryPage: React.FC = () => {
|
|||||||
case 'bazi': return '八字命理';
|
case 'bazi': return '八字命理';
|
||||||
case 'ziwei': return '紫微斗数';
|
case 'ziwei': return '紫微斗数';
|
||||||
case 'yijing': return '易经占卜';
|
case 'yijing': return '易经占卜';
|
||||||
|
case 'qimen': return '奇门遁甲';
|
||||||
default: return '未知类型';
|
default: return '未知类型';
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -210,14 +213,14 @@ const HistoryPage: React.FC = () => {
|
|||||||
|
|
||||||
<AnalysisResultDisplay
|
<AnalysisResultDisplay
|
||||||
analysisResult={selectedReading.analysis}
|
analysisResult={selectedReading.analysis}
|
||||||
analysisType={selectedReading.reading_type as 'bazi' | 'ziwei' | 'yijing'}
|
analysisType={selectedReading.reading_type as 'bazi' | 'ziwei' | 'yijing' | 'qimen'}
|
||||||
birthDate={selectedReading.reading_type !== 'yijing' ? {
|
birthDate={selectedReading.reading_type !== 'yijing' ? {
|
||||||
date: selectedReading.birth_date || '',
|
date: selectedReading.birth_date || '',
|
||||||
time: selectedReading.birth_time || '12:00',
|
time: selectedReading.birth_time || '12:00',
|
||||||
name: selectedReading.name || '',
|
name: selectedReading.name || '',
|
||||||
gender: selectedReading.gender || 'male'
|
gender: selectedReading.gender || 'male'
|
||||||
} : undefined}
|
} : undefined}
|
||||||
question={selectedReading.reading_type === 'yijing' ?
|
question={selectedReading.reading_type === 'yijing' || selectedReading.reading_type === 'qimen' ?
|
||||||
getInputDataValue(selectedReading.input_data, 'question', '综合运势如何?') : undefined}
|
getInputDataValue(selectedReading.input_data, 'question', '综合运势如何?') : undefined}
|
||||||
userId={selectedReading.user_id?.toString()}
|
userId={selectedReading.user_id?.toString()}
|
||||||
divinationMethod={selectedReading.reading_type === 'yijing' ?
|
divinationMethod={selectedReading.reading_type === 'yijing' ?
|
||||||
@@ -333,7 +336,7 @@ const HistoryPage: React.FC = () => {
|
|||||||
...(reading.analysis || reading.results),
|
...(reading.analysis || reading.results),
|
||||||
created_at: reading.created_at
|
created_at: reading.created_at
|
||||||
}}
|
}}
|
||||||
analysisType={reading.reading_type as 'bazi' | 'ziwei' | 'yijing'}
|
analysisType={reading.reading_type as 'bazi' | 'ziwei' | 'yijing' | 'qimen'}
|
||||||
userName={reading.name}
|
userName={reading.name}
|
||||||
className="min-h-[40px] px-2 sm:px-6 py-2.5 text-xs sm:text-sm flex-shrink-0"
|
className="min-h-[40px] px-2 sm:px-6 py-2.5 text-xs sm:text-sm flex-shrink-0"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { Sparkles, Star, Compass, Heart, BarChart3, BookOpen, Shield, Zap, Users, Award, Brain, TrendingUp } from 'lucide-react';
|
import { Sparkles, Star, Compass, Hexagon, Heart, BarChart3, BookOpen, Shield, Zap, Users, Award, Brain, TrendingUp } from 'lucide-react';
|
||||||
import { ChineseButton } from '../components/ui/ChineseButton';
|
import { ChineseButton } from '../components/ui/ChineseButton';
|
||||||
import { ChineseCard, ChineseCardContent, ChineseCardHeader, ChineseCardTitle } from '../components/ui/ChineseCard';
|
import { ChineseCard, ChineseCardContent, ChineseCardHeader, ChineseCardTitle } from '../components/ui/ChineseCard';
|
||||||
import { useAuth } from '../contexts/AuthContext';
|
import { useAuth } from '../contexts/AuthContext';
|
||||||
@@ -35,6 +35,15 @@ const HomePage: React.FC = () => {
|
|||||||
bgColor: 'chinese-golden-glow',
|
bgColor: 'chinese-golden-glow',
|
||||||
iconBg: 'bg-gradient-to-br from-yellow-400 to-amber-500',
|
iconBg: 'bg-gradient-to-br from-yellow-400 to-amber-500',
|
||||||
link: '/analysis'
|
link: '/analysis'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: Hexagon,
|
||||||
|
title: '奇门遁甲',
|
||||||
|
description: '古代帝王之学,通过时空奇门盘分析事物发展趋势。结合九星八门八神布局,为重要决策提供战略指导',
|
||||||
|
color: 'text-red-700',
|
||||||
|
bgColor: 'chinese-golden-glow',
|
||||||
|
iconBg: 'bg-gradient-to-br from-yellow-400 to-amber-500',
|
||||||
|
link: '/analysis'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export interface AIInterpretationResult {
|
|||||||
|
|
||||||
// AI解读请求参数
|
// AI解读请求参数
|
||||||
export interface AIInterpretationRequest {
|
export interface AIInterpretationRequest {
|
||||||
analysisType: 'bazi' | 'ziwei' | 'yijing';
|
analysisType: 'bazi' | 'ziwei' | 'yijing' | 'qimen';
|
||||||
analysisContent: any; // 改为any类型,支持对象数据
|
analysisContent: any; // 改为any类型,支持对象数据
|
||||||
customPrompt?: string;
|
customPrompt?: string;
|
||||||
onStreamUpdate?: (content: string) => void; // 流式更新回调
|
onStreamUpdate?: (content: string) => void; // 流式更新回调
|
||||||
@@ -34,6 +34,9 @@ export const convertAnalysisToMarkdown = (analysisData: any, analysisType: strin
|
|||||||
case 'yijing':
|
case 'yijing':
|
||||||
markdown += generateYijingMarkdown(analysisData);
|
markdown += generateYijingMarkdown(analysisData);
|
||||||
break;
|
break;
|
||||||
|
case 'qimen':
|
||||||
|
markdown += generateQimenMarkdown(analysisData);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
markdown += JSON.stringify(analysisData, null, 2);
|
markdown += JSON.stringify(analysisData, null, 2);
|
||||||
}
|
}
|
||||||
@@ -578,12 +581,113 @@ const generateYijingMarkdown = (data: any): string => {
|
|||||||
return markdown;
|
return markdown;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 生成奇门遁甲分析的Markdown
|
||||||
|
const generateQimenMarkdown = (data: any): string => {
|
||||||
|
const timestamp = new Date().toLocaleString('zh-CN');
|
||||||
|
let markdown = `## 奇门遁甲分析报告\n\n**分析时间:** ${timestamp}\n\n`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 基本信息
|
||||||
|
if (data.timeInfo) {
|
||||||
|
markdown += `### 时空信息\n\n`;
|
||||||
|
if (data.timeInfo.jieqi) markdown += `**节气:** ${data.timeInfo.jieqi}\n`;
|
||||||
|
if (data.qimenPan?.jushu) markdown += `**局数:** ${data.qimenPan.jushu}局\n`;
|
||||||
|
if (data.qimenPan?.yindun !== undefined) {
|
||||||
|
markdown += `**阴阳遁:** ${data.qimenPan.yindun ? '阴遁' : '阳遁'}\n`;
|
||||||
|
}
|
||||||
|
if (data.timeInfo.hour) {
|
||||||
|
markdown += `**时辰:** ${data.timeInfo.hour.gan}${data.timeInfo.hour.zhi}时\n`;
|
||||||
|
}
|
||||||
|
markdown += `\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 奇门盘信息
|
||||||
|
if (data.qimenPan && data.qimenPan.dipan) {
|
||||||
|
markdown += `### 奇门盘布局\n\n`;
|
||||||
|
const palaceNames = ['坎一宫', '坤二宫', '震三宫', '巽四宫', '中五宫', '乾六宫', '兑七宫', '艮八宫', '离九宫'];
|
||||||
|
|
||||||
|
data.qimenPan.dipan.forEach((palace: any, index: number) => {
|
||||||
|
if (palace) {
|
||||||
|
markdown += `**${palaceNames[index]}:**\n`;
|
||||||
|
if (palace.star) markdown += `- 九星:${palace.star}\n`;
|
||||||
|
if (palace.door) markdown += `- 八门:${palace.door}\n`;
|
||||||
|
if (palace.god) markdown += `- 八神:${palace.god}\n`;
|
||||||
|
markdown += `\n`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 用神分析
|
||||||
|
if (data.yongShenAnalysis) {
|
||||||
|
markdown += `### 用神分析\n\n`;
|
||||||
|
|
||||||
|
if (data.yongShenAnalysis.primary) {
|
||||||
|
markdown += `**主用神:**\n`;
|
||||||
|
Object.entries(data.yongShenAnalysis.primary).forEach(([key, value]) => {
|
||||||
|
markdown += `- ${key}:${value}\n`;
|
||||||
|
});
|
||||||
|
markdown += `\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.yongShenAnalysis.secondary) {
|
||||||
|
markdown += `**次用神:**\n`;
|
||||||
|
Object.entries(data.yongShenAnalysis.secondary).forEach(([key, value]) => {
|
||||||
|
markdown += `- ${key}:${value}\n`;
|
||||||
|
});
|
||||||
|
markdown += `\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.yongShenAnalysis.overall) {
|
||||||
|
markdown += `**综合分析:**\n${data.yongShenAnalysis.overall}\n\n`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 格局识别
|
||||||
|
if (data.patterns && data.patterns.length > 0) {
|
||||||
|
markdown += `### 格局识别\n\n`;
|
||||||
|
data.patterns.forEach((pattern: any, index: number) => {
|
||||||
|
markdown += `**${pattern.name}** (${pattern.type === 'auspicious' ? '吉格' : '凶格'})\n`;
|
||||||
|
if (pattern.description) markdown += `${pattern.description}\n`;
|
||||||
|
if (pattern.influence) markdown += `影响:${pattern.influence}\n`;
|
||||||
|
markdown += `\n`;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 预测结果
|
||||||
|
if (data.prediction) {
|
||||||
|
markdown += `### 预测结果\n\n`;
|
||||||
|
|
||||||
|
if (data.prediction.probability) {
|
||||||
|
markdown += `**成功概率:** ${data.prediction.probability}%\n\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.prediction.analysis) {
|
||||||
|
markdown += `**详细分析:**\n${data.prediction.analysis}\n\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.prediction.suggestions && data.prediction.suggestions.length > 0) {
|
||||||
|
markdown += `**建议:**\n`;
|
||||||
|
data.prediction.suggestions.forEach((suggestion: string) => {
|
||||||
|
markdown += `- ${suggestion}\n`;
|
||||||
|
});
|
||||||
|
markdown += `\n`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
markdown += `\n**原始数据:**\n\`\`\`json\n${JSON.stringify(data, null, 2)}\n\`\`\`\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return markdown;
|
||||||
|
};
|
||||||
|
|
||||||
// 获取分析类型标题
|
// 获取分析类型标题
|
||||||
const getAnalysisTitle = (analysisType: string): string => {
|
const getAnalysisTitle = (analysisType: string): string => {
|
||||||
const titles = {
|
const titles = {
|
||||||
'bazi': '八字命理',
|
'bazi': '八字命理',
|
||||||
'ziwei': '紫微斗数',
|
'ziwei': '紫微斗数',
|
||||||
'yijing': '易经占卜'
|
'yijing': '易经占卜',
|
||||||
|
'qimen': '奇门遁甲'
|
||||||
};
|
};
|
||||||
return titles[analysisType as keyof typeof titles] || '命理';
|
return titles[analysisType as keyof typeof titles] || '命理';
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export interface UserProfile {
|
|||||||
export interface AnalysisRecord {
|
export interface AnalysisRecord {
|
||||||
id: number;
|
id: number;
|
||||||
user_id: number;
|
user_id: number;
|
||||||
analysis_type: 'bazi' | 'ziwei' | 'yijing';
|
analysis_type: 'bazi' | 'ziwei' | 'yijing' | 'qimen';
|
||||||
name: string;
|
name: string;
|
||||||
birth_date: string;
|
birth_date: string;
|
||||||
birth_time?: string;
|
birth_time?: string;
|
||||||
@@ -32,7 +32,7 @@ export interface NumerologyReading {
|
|||||||
id: number;
|
id: number;
|
||||||
user_id: number;
|
user_id: number;
|
||||||
profile_id?: string;
|
profile_id?: string;
|
||||||
reading_type: 'bazi' | 'ziwei' | 'yijing' | 'comprehensive';
|
reading_type: 'bazi' | 'ziwei' | 'yijing' | 'qimen' | 'comprehensive';
|
||||||
name: string;
|
name: string;
|
||||||
birth_date: string;
|
birth_date: string;
|
||||||
birth_time?: string;
|
birth_time?: string;
|
||||||
@@ -43,6 +43,7 @@ export interface NumerologyReading {
|
|||||||
bazi?: { bazi_analysis: any };
|
bazi?: { bazi_analysis: any };
|
||||||
ziwei?: { ziwei_analysis: any };
|
ziwei?: { ziwei_analysis: any };
|
||||||
yijing?: { yijing_analysis: any };
|
yijing?: { yijing_analysis: any };
|
||||||
|
qimen?: { qimen_analysis: any };
|
||||||
metadata: {
|
metadata: {
|
||||||
analysis_time: string;
|
analysis_time: string;
|
||||||
version: string;
|
version: string;
|
||||||
|
|||||||
40
update_qimen_constraint.sql
Normal file
40
update_qimen_constraint.sql
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
-- 更新numerology_readings表的CHECK约束以支持qimen类型
|
||||||
|
-- 由于SQLite不支持直接修改CHECK约束,需要重建表
|
||||||
|
|
||||||
|
BEGIN TRANSACTION;
|
||||||
|
|
||||||
|
-- 创建临时表,包含新的CHECK约束
|
||||||
|
CREATE TABLE numerology_readings_temp (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
user_id INTEGER NOT NULL,
|
||||||
|
reading_type TEXT NOT NULL CHECK (reading_type IN ('bazi', 'ziwei', 'yijing', 'wuxing', 'qimen')),
|
||||||
|
name TEXT,
|
||||||
|
birth_date TEXT,
|
||||||
|
birth_time TEXT,
|
||||||
|
birth_place TEXT,
|
||||||
|
gender TEXT,
|
||||||
|
input_data TEXT,
|
||||||
|
results TEXT,
|
||||||
|
analysis TEXT,
|
||||||
|
status TEXT DEFAULT 'completed' CHECK (status IN ('pending', 'processing', 'completed', 'failed')),
|
||||||
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 复制现有数据到临时表
|
||||||
|
INSERT INTO numerology_readings_temp
|
||||||
|
SELECT id, user_id, reading_type, name, birth_date, birth_time, birth_place, gender,
|
||||||
|
input_data, results, analysis, status, created_at, updated_at
|
||||||
|
FROM numerology_readings;
|
||||||
|
|
||||||
|
-- 删除原表
|
||||||
|
DROP TABLE numerology_readings;
|
||||||
|
|
||||||
|
-- 重命名临时表为原表名
|
||||||
|
ALTER TABLE numerology_readings_temp RENAME TO numerology_readings;
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
|
|
||||||
|
-- 验证更新
|
||||||
|
SELECT name FROM sqlite_master WHERE type='table' AND name='numerology_readings';
|
||||||
Reference in New Issue
Block a user