diff --git a/src/services/aiService.ts b/src/services/aiService.ts index e4e0e37..affd832 100644 --- a/src/services/aiService.ts +++ b/src/services/aiService.ts @@ -313,9 +313,6 @@ export interface DailyInsightContext { /** * Get AI-powered daily insight */ -/** - * Get AI-powered daily insight via dedicated endpoint - */ export async function getDailyInsight(context: DailyInsightContext): Promise<{ spending: string; budget: string }> { // Hash needs to include new fields const currentHash = JSON.stringify(context); @@ -329,32 +326,64 @@ export async function getDailyInsight(context: DailyInsightContext): Promise<{ s return dailyInsightCache.data; } - // 2. Prepare Context Data const weekday = new Date().toLocaleDateString('zh-CN', { weekday: 'long' }); const weekDiff = context.lastWeekSpend !== undefined ? (context.todaySpend - context.lastWeekSpend) : 0; - const contextData = { - weekday, - streakDays: context.streakDays || 0, - todaySpend: context.todaySpend, - yesterdaySpend: context.yesterdaySpend, - lastWeekSpend: context.lastWeekSpend || 0, - weekSpendDiff: weekDiff, - monthlyBudget: context.monthlyBudget, - monthlySpent: context.monthlySpent, - monthProgress: context.monthProgress, - topCategory: context.topCategory ? `${context.topCategory.name} (¥${context.topCategory.amount})` : '无', - maxTransaction: context.maxTransaction ? `${context.maxTransaction.note || '未备注'} (¥${context.maxTransaction.amount})` : '无', - }; + const prompt = `System: 你是 Novault 首席财务AI,也是用户的贴身管家。你的点评需要非常有温度、有依据。 +Context: +- 今天是: ${weekday} +- 连续记账: ${context.streakDays || 0} 天 +- 今日支出: ${context.todaySpend} + - 对比昨日: ${context.yesterdaySpend} + - 对比上周同日: ${context.lastWeekSpend || '无数据'} (差额: ${weekDiff}) +- 月预算: ${context.monthlyBudget} (已用: ${context.monthlySpent}, 进度: ${(context.monthlySpent / (context.monthlyBudget || 1) * 100).toFixed(0)}%) +- 月时间进度: ${(context.monthProgress * 100).toFixed(0)}% +- 今日支出之王: ${context.topCategory ? `【${context.topCategory.name}】 ¥${context.topCategory.amount}` : '无'} +- 今日最大一笔: ${context.maxTransaction ? `${context.maxTransaction.note || '未备注'} (¥${context.maxTransaction.amount})` : '无'} + +Task: +请输出 JSON 对象(无 markdown 标记): +1. "spending": 针对今日支出的点评(40字内)。 + - 必须结合【${weekday}】的场景(如周五可以放松,周一要收心)。 + - 如果有【连续记账】成就(>3天),请顺带夸奖。 + - 如果对比上周同日波动大,请指出。 + - 结合最大支出进行调侃。 + +2. "budget": 针对预算状况的建议(40字内)。 + - 结合月度进度,给出具体行动指南。 +`; try { - // 3. Call Dedicated Backend Endpoint - const response = await api.post>('/ai/insight', { - context_data: contextData - }); + const response = await sendChatMessage(prompt); - if (response.success && response.data) { - const result = response.data; + if (response.message) { + let content = response.message.trim(); + + // Extract JSON object using regex to handle potential markdown or extra text + const jsonMatch = content.match(/\{[\s\S]*\}/); + if (jsonMatch) { + content = jsonMatch[0]; + } else { + // Fallback cleanup if regex fails but it might still be JSON-ish + content = content.replace(/^```json\s*/, '').replace(/\s*```$/, ''); + } + + let parsed; + try { + parsed = JSON.parse(content); + } catch (e) { + console.warn('AI returned invalid JSON, falling back to raw text split or default', content); + // Fallback: simple split if possible or default + parsed = { + spending: content.slice(0, 50) + '...', + budget: 'AI 分析数据格式异常,请稍后再试。' + }; + } + + const result = { + spending: parsed.spending || '暂无点评', + budget: parsed.budget || '暂无建议' + }; // Update Cache dailyInsightCache = { @@ -365,15 +394,10 @@ export async function getDailyInsight(context: DailyInsightContext): Promise<{ s return result; } - - throw new Error(response.error || 'Failed to generate insight'); + throw new Error('No insight received'); } catch (error) { console.error('Failed to get AI insight:', error); - // Return fallback instead of throwing to prevent UI crash - return { - spending: '今日消费情况暂未分析完成', - budget: '暂无预算建议' - }; + throw error; } }