feat: 接入 DeepSeek,新增 AI 分类建议、自然语言查询与智能记账
- 后端新增 AI 模块:DeepSeek 客户端、用户级配置表 t_ai_config、用量记录表 t_ai_usage - 批量设置支持 AI 推荐分类与标签,并可一键应用 - 交易列表支持自然语言查询,解析为筛选条件确认后应用 - 支持自然语言记账,解析后预填新增表单 - 新增 AI 设置页:启用开关、API Key、接口地址、模型、连通性测试与用量统计 - 交易列表重排:AI 查询区、已激活筛选标签、备注与查询历史缓存 - 账单导入默认分类下拉补充分类图标 - 顶部用户入口改为下拉菜单(AI 设置 / 退出登录)
This commit is contained in:
@@ -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 } })
|
||||
}
|
||||
Reference in New Issue
Block a user