UNPKG

yijing-bazi-mcp

Version:

易经八字分析 MCP 服务器 - 提供传统命理分析和易经卦象解读的AI工具

558 lines (493 loc) 15.1 kB
/** * 验证工具类 * 用于参数验证和数据校验 */ const Joi = require('joi'); const moment = require('moment'); class ValidationUtils { constructor() { this.schemas = this.initializeSchemas(); } /** * 初始化验证模式 */ initializeSchemas() { return { // 日期时间验证 birthDateTime: Joi.alternatives().try( Joi.date().iso(), Joi.string().pattern(/^\d{4}-\d{2}-\d{2}(\s\d{2}:\d{2}(:\d{2})?)?$/) ).required().messages({ 'any.required': '出生日期时间是必需的', 'date.base': '请提供有效的日期格式', 'string.pattern.base': '日期格式应为 YYYY-MM-DD 或 YYYY-MM-DD HH:mm:ss' }), // 性别验证 gender: Joi.string().valid('male', 'female', '男', '女').required().messages({ 'any.required': '性别是必需的', 'any.only': '性别只能是 male、female、男 或 女' }), // 时区验证 timezone: Joi.string().default('Asia/Shanghai').messages({ 'string.base': '时区必须是字符串' }), // 问题类型验证 questionType: Joi.string().valid( 'career', 'relationship', 'health', 'wealth', 'education', 'general' ).default('general').messages({ 'any.only': '问题类型必须是 career、relationship、health、wealth、education 或 general 之一' }), // 分析类型验证 analysisType: Joi.string().valid( 'personality', 'career', 'wealth', 'relationship', 'health', 'comprehensive' ).default('comprehensive').messages({ 'any.only': '分析类型必须是 personality、career、wealth、relationship、health 或 comprehensive 之一' }), // 预测期间验证 forecastPeriod: Joi.string().valid( 'monthly', 'yearly', 'decade', 'custom' ).default('yearly').messages({ 'any.only': '预测期间必须是 monthly、yearly、decade 或 custom 之一' }), // 年份验证 year: Joi.number().integer().min(1900).max(2100).messages({ 'number.base': '年份必须是数字', 'number.integer': '年份必须是整数', 'number.min': '年份不能早于1900年', 'number.max': '年份不能晚于2100年' }), // 月份验证 month: Joi.number().integer().min(1).max(12).messages({ 'number.base': '月份必须是数字', 'number.integer': '月份必须是整数', 'number.min': '月份不能小于1', 'number.max': '月份不能大于12' }), // 占卜方法验证 divinationMethod: Joi.string().valid( 'coin', 'yarrow', 'number', 'time' ).default('coin').messages({ 'any.only': '占卜方法必须是 coin、yarrow、number 或 time 之一' }), // 问题验证 question: Joi.string().min(1).max(500).required().messages({ 'any.required': '问题是必需的', 'string.empty': '问题不能为空', 'string.min': '问题至少需要1个字符', 'string.max': '问题不能超过500个字符' }), // 焦点验证 focus: Joi.string().valid( 'general', 'career', 'relationship', 'health', 'wealth', 'decision' ).default('general').messages({ 'any.only': '焦点必须是 general、career、relationship、health、wealth 或 decision 之一' }), // 学习主题验证 topic: Joi.string().valid( 'yijing_basics', 'hexagram_interpretation', 'bazi_basics', 'ten_gods', 'five_elements', 'spirits', 'patterns', 'forecasting' ).required().messages({ 'any.required': '学习主题是必需的', 'any.only': '学习主题必须是预定义的主题之一' }), // 学习级别验证 level: Joi.string().valid('beginner', 'intermediate', 'advanced').default('beginner').messages({ 'any.only': '学习级别必须是 beginner、intermediate 或 advanced 之一' }), // 学习风格验证 learningStyle: Joi.string().valid( 'theoretical', 'practical', 'case_study', 'interactive' ).default('theoretical').messages({ 'any.only': '学习风格必须是 theoretical、practical、case_study 或 interactive 之一' }), // 案例数据验证 caseData: Joi.object().required().messages({ 'any.required': '案例数据是必需的', 'object.base': '案例数据必须是对象' }), // 咨询类型验证 consultationType: Joi.string().valid( 'career', 'relationship', 'health', 'comprehensive' ).required().messages({ 'any.required': '咨询类型是必需的', 'any.only': '咨询类型必须是 career、relationship、health 或 comprehensive 之一' }) }; } /** * 验证生成卦象的参数 * @param {Object} params - 参数对象 * @returns {Object} 验证结果 */ validateGenerateHexagram(params) { const schema = Joi.object({ method: this.schemas.divinationMethod, question: this.schemas.question, focus: this.schemas.focus }); return this.validate(schema, params); } /** * 验证解释卦象的参数 * @param {Object} params - 参数对象 * @returns {Object} 验证结果 */ validateInterpretHexagram(params) { const schema = Joi.object({ hexagram_id: Joi.number().integer().min(1).max(64).required().messages({ 'any.required': '卦象ID是必需的', 'number.base': '卦象ID必须是数字', 'number.integer': '卦象ID必须是整数', 'number.min': '卦象ID不能小于1', 'number.max': '卦象ID不能大于64' }), question: this.schemas.question, focus: this.schemas.focus, changed_lines: Joi.array().items(Joi.number().integer().min(1).max(6)).messages({ 'array.base': '变爻必须是数组', 'number.base': '变爻位置必须是数字', 'number.integer': '变爻位置必须是整数', 'number.min': '变爻位置不能小于1', 'number.max': '变爻位置不能大于6' }) }); return this.validate(schema, params); } /** * 验证易经建议的参数 * @param {Object} params - 参数对象 * @returns {Object} 验证结果 */ validateYijingAdvice(params) { const schema = Joi.object({ question: this.schemas.question, question_type: this.schemas.questionType, context: Joi.string().max(1000).messages({ 'string.max': '上下文不能超过1000个字符' }) }); return this.validate(schema, params); } /** * 验证生成八字的参数 * @param {Object} params - 参数对象 * @returns {Object} 验证结果 */ validateGenerateBazi(params) { const schema = Joi.object({ birth_datetime: this.schemas.birthDateTime, gender: this.schemas.gender, timezone: this.schemas.timezone }); return this.validate(schema, params); } /** * 验证分析八字的参数 * @param {Object} params - 参数对象 * @returns {Object} 验证结果 */ validateAnalyzeBazi(params) { const schema = Joi.object({ birth_datetime: this.schemas.birthDateTime, gender: this.schemas.gender, analysis_type: this.schemas.analysisType, timezone: this.schemas.timezone }); return this.validate(schema, params); } /** * 验证八字预测的参数 * @param {Object} params - 参数对象 * @returns {Object} 验证结果 */ validateBaziForecast(params) { const schema = Joi.object({ birth_datetime: this.schemas.birthDateTime, gender: this.schemas.gender, period: this.schemas.forecastPeriod, target_year: this.schemas.year, target_month: this.schemas.month, timezone: this.schemas.timezone }); return this.validate(schema, params); } /** * 验证综合分析的参数 * @param {Object} params - 参数对象 * @returns {Object} 验证结果 */ validateCombinedAnalysis(params) { const schema = Joi.object({ birth_datetime: this.schemas.birthDateTime, gender: this.schemas.gender, question: this.schemas.question, question_type: this.schemas.questionType, timezone: this.schemas.timezone }); return this.validate(schema, params); } /** * 验证命理咨询的参数 * @param {Object} params - 参数对象 * @returns {Object} 验证结果 */ validateDestinyConsult(params) { const schema = Joi.object({ birth_datetime: this.schemas.birthDateTime, gender: this.schemas.gender, consultation_type: this.schemas.consultationType, specific_question: Joi.string().max(500).messages({ 'string.max': '具体问题不能超过500个字符' }), timezone: this.schemas.timezone }); return this.validate(schema, params); } /** * 验证知识学习的参数 * @param {Object} params - 参数对象 * @returns {Object} 验证结果 */ validateKnowledgeLearn(params) { const schema = Joi.object({ topic: this.schemas.topic, level: this.schemas.level, learning_style: this.schemas.learningStyle }); return this.validate(schema, params); } /** * 验证案例研究的参数 * @param {Object} params - 参数对象 * @returns {Object} 验证结果 */ validateCaseStudy(params) { const schema = Joi.object({ case_data: this.schemas.caseData, analysis_focus: Joi.string().max(200).messages({ 'string.max': '分析焦点不能超过200个字符' }) }); return this.validate(schema, params); } /** * 执行验证 * @param {Object} schema - Joi验证模式 * @param {Object} data - 要验证的数据 * @returns {Object} 验证结果 */ validate(schema, data) { const { error, value } = schema.validate(data, { abortEarly: false, allowUnknown: false, stripUnknown: true }); if (error) { return { isValid: false, errors: error.details.map(detail => ({ field: detail.path.join('.'), message: detail.message, value: detail.context.value })), data: null }; } return { isValid: true, errors: [], data: value }; } /** * 验证日期格式 * @param {string} dateString - 日期字符串 * @returns {Object} 验证结果 */ validateDateFormat(dateString) { const formats = [ 'YYYY-MM-DD', 'YYYY-MM-DD HH:mm', 'YYYY-MM-DD HH:mm:ss', 'YYYY/MM/DD', 'YYYY/MM/DD HH:mm', 'YYYY/MM/DD HH:mm:ss' ]; for (const format of formats) { const parsed = moment(dateString, format, true); if (parsed.isValid()) { return { isValid: true, format, parsed: parsed.toDate(), message: '日期格式有效' }; } } return { isValid: false, format: null, parsed: null, message: '无效的日期格式,支持的格式:YYYY-MM-DD [HH:mm[:ss]]' }; } /** * 验证时区 * @param {string} timezone - 时区字符串 * @returns {Object} 验证结果 */ validateTimezone(timezone) { try { const testDate = moment().tz(timezone); return { isValid: true, timezone, message: '时区有效' }; } catch (error) { return { isValid: false, timezone, message: `无效的时区: ${timezone}` }; } } /** * 验证卦象ID * @param {number} hexagramId - 卦象ID * @returns {Object} 验证结果 */ validateHexagramId(hexagramId) { const id = parseInt(hexagramId); if (isNaN(id) || id < 1 || id > 64) { return { isValid: false, message: '卦象ID必须是1-64之间的整数' }; } return { isValid: true, hexagramId: id, message: '卦象ID有效' }; } /** * 验证变爻 * @param {Array} changedLines - 变爻数组 * @returns {Object} 验证结果 */ validateChangedLines(changedLines) { if (!Array.isArray(changedLines)) { return { isValid: false, message: '变爻必须是数组' }; } const validLines = changedLines.filter(line => { const num = parseInt(line); return !isNaN(num) && num >= 1 && num <= 6; }); if (validLines.length !== changedLines.length) { return { isValid: false, message: '变爻位置必须是1-6之间的整数' }; } // 去重 const uniqueLines = [...new Set(validLines)]; return { isValid: true, changedLines: uniqueLines, message: '变爻有效' }; } /** * 验证性别 * @param {string} gender - 性别 * @returns {Object} 验证结果 */ validateGender(gender) { const normalizedGender = this.normalizeGender(gender); if (!normalizedGender) { return { isValid: false, message: '性别只能是 male、female、男 或 女' }; } return { isValid: true, gender: normalizedGender, message: '性别有效' }; } /** * 标准化性别 * @param {string} gender - 性别 * @returns {string|null} 标准化后的性别 */ normalizeGender(gender) { const genderMap = { 'male': 'male', 'female': 'female', '男': 'male', '女': 'female', 'M': 'male', 'F': 'female', 'm': 'male', 'f': 'female' }; return genderMap[gender] || null; } /** * 清理和验证输入数据 * @param {Object} data - 输入数据 * @returns {Object} 清理后的数据 */ sanitizeInput(data) { const sanitized = {}; for (const [key, value] of Object.entries(data)) { if (typeof value === 'string') { // 去除首尾空格 sanitized[key] = value.trim(); // 防止XSS攻击,移除潜在的脚本标签 sanitized[key] = sanitized[key].replace(/<script[^>]*>.*?<\/script>/gi, ''); sanitized[key] = sanitized[key].replace(/javascript:/gi, ''); sanitized[key] = sanitized[key].replace(/on\w+=/gi, ''); } else { sanitized[key] = value; } } return sanitized; } /** * 创建错误响应 * @param {string} message - 错误消息 * @param {Array} details - 错误详情 * @returns {Object} 错误响应 */ createErrorResponse(message, details = []) { return { success: false, error: { message, details, timestamp: new Date().toISOString() } }; } /** * 创建成功响应 * @param {*} data - 响应数据 * @param {string} message - 成功消息 * @returns {Object} 成功响应 */ createSuccessResponse(data, message = '操作成功') { return { success: true, data, message, timestamp: new Date().toISOString() }; } } module.exports = ValidationUtils;