245 lines
7.3 KiB
JavaScript
245 lines
7.3 KiB
JavaScript
const { getZodiacList } = require('./zodiacData');
|
||
|
||
// AI 配置
|
||
const API_KEY = 'sk-f659b4c3aa954baaa6cbdbd5cae0b7d1';
|
||
const BASE_URL = 'https://api.deepseek.com/chat/completions';
|
||
|
||
/**
|
||
* 获取今日日期字符串 (YYYY-MM-DD)
|
||
*/
|
||
function getTodayStr() {
|
||
const now = new Date();
|
||
return `${now.getFullYear()}-${now.getMonth() + 1}-${now.getDate()}`;
|
||
}
|
||
|
||
/**
|
||
* 构建系统 Prompt
|
||
*/
|
||
function buildPrompt(zodiacName) {
|
||
return `你是一位温暖、疗愈且富有洞察力的星座运势师。
|
||
请根据今天的日期,为【${zodiacName}】生成一份专属运势。
|
||
|
||
【合规要求 - 极重要】
|
||
1. 语言风格:温柔、诗意、治愈,仅供娱乐和心理参考。
|
||
2. 禁止绝对化:不要使用“注定”、“一定会”、“必然”等词汇,使用“可能”、“倾向于”、“建议”等委婉表达。
|
||
3. 禁止迷信承诺:严禁承诺具体的发财、升职、脱单结果。
|
||
4. 禁止现实决策:严禁给出具体的医疗、法律、投资理财建议。
|
||
5. 核心基调:通过细微的生活洞察,给人力量和抚慰,侧重心理状态疏导。
|
||
|
||
【输出结构】
|
||
请严格按照以下 JSON 格式输出,不要包含 markdown 标记:
|
||
{
|
||
"keyword": "今日关键词 (2个字)",
|
||
"core_energy": "核心能量 (15-20字,一句话概括)",
|
||
"ratings": {
|
||
"overall": 4, // 1-5的整数
|
||
"love": 3,
|
||
"career": 5,
|
||
"wealth": 3
|
||
},
|
||
"fortune_text": {
|
||
"love": "感情运势 (30-50字)",
|
||
"career": "事业运势 (30-50字)",
|
||
"wealth": "财运提醒 (30-50字)"
|
||
},
|
||
"lucky_elements": {
|
||
"color": "颜色",
|
||
"number": "数字",
|
||
"time": "吉时 (如 15:00-17:00)"
|
||
},
|
||
"action": "行动建议 (20-30字)"
|
||
}`;
|
||
}
|
||
|
||
/**
|
||
* 安全解析 JSON
|
||
*/
|
||
function safeJSONParse(text) {
|
||
try {
|
||
const cleanText = text.replace(/```json/g, '').replace(/```/g, '').trim();
|
||
return JSON.parse(cleanText);
|
||
} catch (e) {
|
||
console.error('JSON解析失败:', e);
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取本地兜底数据 (适配新格式)
|
||
*/
|
||
function getLocalFallback(zodiacName) {
|
||
const list = getZodiacList();
|
||
const target = list.find(z => z.name === zodiacName);
|
||
|
||
if (target && target.fortunes && target.fortunes.length > 0) {
|
||
const randomIdx = Math.floor(Math.random() * target.fortunes.length);
|
||
const oldData = target.fortunes[randomIdx];
|
||
|
||
// 适配旧数据到新格式
|
||
return {
|
||
keyword: oldData.keyword,
|
||
core_energy: oldData.today,
|
||
ratings: {
|
||
overall: 4,
|
||
love: 3,
|
||
career: 4,
|
||
wealth: 3
|
||
},
|
||
fortune_text: {
|
||
love: oldData.summary, // 暂用 summary 填充
|
||
career: oldData.today,
|
||
wealth: oldData.notice
|
||
},
|
||
lucky_elements: {
|
||
color: "今日专属色",
|
||
number: "7",
|
||
time: "09:00-11:00"
|
||
},
|
||
action: oldData.action
|
||
};
|
||
}
|
||
return null;
|
||
}
|
||
|
||
/**
|
||
* 获取星座运势 (优先缓存 -> AI -> 本地兜底)
|
||
* @param {string} zodiacName 星座名称
|
||
* @returns {Promise<object>} 运势数据对象
|
||
*/
|
||
async function fetchZodiacFortune(zodiacName) {
|
||
const today = getTodayStr();
|
||
const cacheKey = `zodiac_fortune_${zodiacName}_${today}`;
|
||
|
||
// 1. 检查缓存
|
||
try {
|
||
const cached = wx.getStorageSync(cacheKey);
|
||
if (cached) {
|
||
console.log(`[ZodiacAI] Hit cache for ${zodiacName}`);
|
||
return cached;
|
||
}
|
||
} catch (e) {
|
||
console.error('读取缓存失败:', e);
|
||
}
|
||
|
||
console.log(`[ZodiacAI] Fetching AI for ${zodiacName}...`);
|
||
|
||
// 2. 调用 AI 接口
|
||
try {
|
||
const res = await new Promise((resolve, reject) => {
|
||
wx.request({
|
||
url: BASE_URL,
|
||
method: 'POST',
|
||
header: {
|
||
'Content-Type': 'application/json',
|
||
'Authorization': `Bearer ${API_KEY}`
|
||
},
|
||
data: {
|
||
model: "deepseek-chat",
|
||
messages: [
|
||
{ role: "system", content: buildPrompt(zodiacName) },
|
||
{ role: "user", content: `今天是 ${today},请解读。` }
|
||
],
|
||
temperature: 1.3 // 稍微提高创造性
|
||
},
|
||
timeout: 30000,
|
||
success: resolve,
|
||
fail: reject
|
||
});
|
||
});
|
||
|
||
if (res.statusCode === 200 && res.data.choices && res.data.choices[0]) {
|
||
const content = res.data.choices[0].message.content;
|
||
const parsedData = safeJSONParse(content);
|
||
|
||
if (parsedData) {
|
||
// 3. 写入缓存 (成功才缓存)
|
||
wx.setStorageSync(cacheKey, parsedData);
|
||
return parsedData;
|
||
}
|
||
}
|
||
console.warn('[ZodiacAI] AI response invalid, using fallback.');
|
||
|
||
} catch (err) {
|
||
console.error('[ZodiacAI] Request failed:', err);
|
||
}
|
||
|
||
// 4. 失败兜底 (返回本地数据)
|
||
return null;
|
||
}
|
||
|
||
/**
|
||
* 获取今日星象概览 (每日更新一次)
|
||
*/
|
||
async function fetchDailyOverview() {
|
||
const today = getTodayStr();
|
||
const cacheKey = `daily_overview_${today}`;
|
||
|
||
// 1. 检查缓存
|
||
try {
|
||
const cached = wx.getStorageSync(cacheKey);
|
||
if (cached) {
|
||
return cached;
|
||
}
|
||
} catch (e) {
|
||
console.error('读取缓存失败:', e);
|
||
}
|
||
|
||
const prompt = `你是一位专业占星师。请根据今天日期,生成一段通用的【今日星象概览】。
|
||
【风格要求】
|
||
- 客观、简练、有指导意义
|
||
- 描述今天的月亮位置、主要行星相位(如水星三合木星等)及其对大众的影响
|
||
|
||
【输出格式】
|
||
严格 JSON 格式:
|
||
{
|
||
"content": "星象描述 (40-60字)",
|
||
"tags": ["标签1", "标签2", "标签3"] // 3个短标签,如:利于社交、适合学习、谨慎决策
|
||
}`;
|
||
|
||
// 2. 调用 AI
|
||
try {
|
||
const res = await new Promise((resolve, reject) => {
|
||
wx.request({
|
||
url: BASE_URL,
|
||
method: 'POST',
|
||
header: {
|
||
'Content-Type': 'application/json',
|
||
'Authorization': `Bearer ${API_KEY}`
|
||
},
|
||
data: {
|
||
model: "deepseek-chat",
|
||
messages: [
|
||
{ role: "system", content: prompt },
|
||
{ role: "user", content: `今天是 ${today}。` }
|
||
],
|
||
temperature: 1.0
|
||
},
|
||
timeout: 30000,
|
||
success: resolve,
|
||
fail: reject
|
||
});
|
||
});
|
||
|
||
if (res.statusCode === 200 && res.data.choices) {
|
||
const parsed = safeJSONParse(res.data.choices[0].message.content);
|
||
if (parsed) {
|
||
wx.setStorageSync(cacheKey, parsed);
|
||
return parsed;
|
||
}
|
||
}
|
||
} catch (err) {
|
||
console.error('星象概览获取失败:', err);
|
||
}
|
||
|
||
// 兜底数据
|
||
return {
|
||
content: "今日月亮能量温和,适合处理内心的情绪。水星状态良好,沟通效率较高。",
|
||
tags: ["平稳", "沟通顺畅", "宜内省"]
|
||
};
|
||
}
|
||
|
||
module.exports = {
|
||
fetchZodiacFortune,
|
||
fetchDailyOverview
|
||
};
|