feat: 新增预算详情模态框、交易表单、小猪罐功能,并支持生成预算洞察。

This commit is contained in:
2026-01-28 21:44:47 +08:00
parent 2d84f66bdc
commit e163fadd01
14 changed files with 1592 additions and 210 deletions

View File

@@ -289,17 +289,125 @@ Output Requirements:
// Here we choose to throw so fallback static text appears.
throw error;
}
}
// Cache storage for daily insight
let dailyInsightCache: {
data: { spending: string; budget: string };
timestamp: number;
contentHash: string;
} | null = null;
export default {
getSessionId,
clearSession,
sendChatMessage,
transcribeAudio,
confirmTransaction,
cancelSession,
processVoiceInput,
isConfirmationCardComplete,
formatConfirmationCard,
getFinancialAdvice,
};
export interface DailyInsightContext {
todaySpend: number;
yesterdaySpend: number;
monthlyBudget: number;
monthlySpent: number;
monthProgress: number; // 0-1
topCategory?: { name: string; amount: number };
maxTransaction?: { note: string; amount: number };
lastWeekSpend?: number;
streakDays?: number;
}
/**
* Get AI-powered daily insight
*/
export async function getDailyInsight(context: DailyInsightContext): Promise<{ spending: string; budget: string }> {
// Hash needs to include new fields
const currentHash = JSON.stringify(context);
const NOW = Date.now();
const CACHE_TTL = 30 * 60 * 1000; // 30 Minutes
// 1. Check Cache
if (dailyInsightCache &&
(NOW - dailyInsightCache.timestamp < CACHE_TTL) &&
dailyInsightCache.contentHash === currentHash) {
return dailyInsightCache.data;
}
const weekday = new Date().toLocaleDateString('zh-CN', { weekday: 'long' });
const weekDiff = context.lastWeekSpend !== undefined ? (context.todaySpend - context.lastWeekSpend) : 0;
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 {
const response = await sendChatMessage(prompt);
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 = {
data: result,
timestamp: NOW,
contentHash: currentHash
};
return result;
}
throw new Error('No insight received');
} catch (error) {
console.error('Failed to get AI insight:', error);
throw error;
}
}
export default {
getSessionId,
clearSession,
sendChatMessage,
transcribeAudio,
confirmTransaction,
cancelSession,
processVoiceInput,
isConfirmationCardComplete,
formatConfirmationCard,
getFinancialAdvice,
};