feat: 接入 DeepSeek,新增 AI 分类建议、自然语言查询与智能记账

- 后端新增 AI 模块:DeepSeek 客户端、用户级配置表 t_ai_config、用量记录表 t_ai_usage
- 批量设置支持 AI 推荐分类与标签,并可一键应用
- 交易列表支持自然语言查询,解析为筛选条件确认后应用
- 支持自然语言记账,解析后预填新增表单
- 新增 AI 设置页:启用开关、API Key、接口地址、模型、连通性测试与用量统计
- 交易列表重排:AI 查询区、已激活筛选标签、备注与查询历史缓存
- 账单导入默认分类下拉补充分类图标
- 顶部用户入口改为下拉菜单(AI 设置 / 退出登录)
This commit is contained in:
2026-09-17 18:26:03 +08:00
parent 10fa1b5e96
commit 49c70a732d
38 changed files with 3951 additions and 27 deletions
+129
View File
@@ -0,0 +1,129 @@
// AI 智能建议相关 API 接口:配置读写、连通性测试、建议生成与应用
import request from './request'
/** AI 配置数据结构(API Key 已脱敏) */
export interface AiConfig {
enabled: boolean
apiKey: string | null
hasApiKey: boolean
baseUrl: string
model: string
}
/** 单条 AI 建议 */
export interface AiSuggestion {
transactionId: number
categoryId: number | null
categoryName: string | null
tagIds: number[]
tagNames: string[]
reason: string
}
/** 获取当前用户的 AI 配置 */
export function getAiConfig() {
return request.get('/ai/config')
}
/** 保存 AI 配置,apiKey 留空表示不修改原值 */
export function saveAiConfig(data: { enabled: boolean; apiKey?: string; baseUrl?: string; model?: string }) {
return request.put('/ai/config', data)
}
/** 测试 DeepSeek 连通性(耗时较长,单独放宽超时) */
export function testAiConfig() {
return request.post('/ai/config/test', {}, { timeout: 60000 })
}
/** 生成分类与标签建议(调用大模型,超时放宽) */
export function suggestByAi(ids: number[]) {
return request.post('/ai/suggest', { ids }, { timeout: 120000 })
}
/** 一键应用建议:覆盖分类并追加标签 */
export function applyAiSuggestions(suggestions: { transactionId: number; categoryId: number | null; tagIds: number[] }[]) {
return request.post('/ai/apply', { suggestions })
}
/** 自然语言查询解析结果 */
export interface AiQueryResult {
summary: string
types: string[]
categoryIds: number[]
categoryNames: string[]
uncategorized: boolean
paymentMethods: string[]
startTime: string | null
endTime: string | null
includeInStats: boolean | null
minAmount: number | null
maxAmount: number | null
remark: string | null
tagIds: number[]
tagNames: string[]
unmatched: string[]
}
/** 将自然语言查询解析为筛选条件 */
export function queryByAi(query: string) {
return request.post('/ai/query', { query }, { timeout: 120000 })
}
/** 自然语言记账解析结果 */
export interface AiRecordResult {
summary: string
type: string
amount: number | null
categoryId: number | null
categoryName: string | null
paymentMethod: string | null
transactionTime: string | null
remark: string | null
includeInStats: boolean | null
tagIds: number[]
tagNames: string[]
unmatched: string[]
}
/** 将自然语言记账内容解析为交易记录 */
export function parseRecordByAi(text: string) {
return request.post('/ai/record', { text }, { timeout: 120000 })
}
/** AI 使用累计汇总 */
export interface AiUsageSummary {
totalCalls: number
successCalls: number
failedCalls: number
promptTokens: number
completionTokens: number
totalTokens: number
cachedTokens: number
}
/** 单条 AI 使用明细 */
export interface AiUsageItem {
id: number
type: string
model: string
transactionCount: number
promptTokens: number
completionTokens: number
totalTokens: number
cachedTokens: number
success: boolean
errorMessage: string | null
durationMs: number | null
createdAt: string
}
/** AI 使用情况(累计汇总 + 最近明细) */
export interface AiUsageResult {
summary: AiUsageSummary
recent: AiUsageItem[]
}
/** 获取 AI 使用情况 */
export function getAiUsage(limit = 20) {
return request.get('/ai/usage', { params: { limit } })
}