// AI 智能建议相关 API 接口:配置读写、连通性测试、建议生成与应用 import request from './request' /** AI 配置数据结构(API Key 已脱敏) */ export interface AiConfig { enabled: boolean apiKey: string | null hasApiKey: boolean baseUrl: string model: string visionModel: string } /** AI 建议新建的标签 */ export interface AiNewTag { name: string color: string } /** 单条 AI 建议(按类型、备注、交易渠道聚合后的一组记录) */ export interface AiSuggestion { transactionId: number transactionIds: number[] count: number categoryId: number | null categoryName: string | null newCategoryName: string | null newCategoryIcon: string | null tagIds: number[] tagNames: string[] channel: string | null channelName: string | null newTags: AiNewTag[] 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; visionModel?: string }) { return request.put('/ai/config', data) } /** 测试 DeepSeek 连通性(耗时较长,单独放宽超时) */ export function testAiConfig() { return request.post('/ai/config/test', {}, { timeout: 60000 }) } /** 生成分类与标签建议(scope:BOTH/CATEGORY/TAG;重复记录聚合后按批调用,超时按批数放宽) */ export function suggestByAi(ids: number[], scope = 'BOTH') { const batches = Math.max(1, Math.ceil(ids.length / 100)) return request.post('/ai/suggest', { ids, scope }, { timeout: Math.min(Math.max(120000, batches * 90000), 600000) }) } /** AI 生成的分类建议 */ export interface AiCategoryResult { name: string type: string icon: string reason: string } /** 根据自然语言描述生成分类(调用大模型,超时放宽) */ export function parseCategoryByAi(text: string) { return request.post('/ai/category', { text }, { timeout: 60000 }) } /** AI 生成的标签建议 */ export interface AiTagResult { name: string color: string reason: string } /** 根据自然语言描述生成标签(调用大模型,超时放宽) */ export function parseTagByAi(text: string) { return request.post('/ai/tag', { text }, { timeout: 60000 }) } /** AI 生成的交易渠道建议 */ export interface AiChannelResult { name: string reason: string } /** 根据自然语言描述生成交易渠道(调用大模型,超时放宽) */ export function parseChannelByAi(text: string) { return request.post('/ai/channel', { text }, { timeout: 60000 }) } /** AI 生成的账单关系名称与备注建议 */ export interface AiRelationResult { name: string remark: string reason: string } /** 根据关系类型与成员账单特征生成关系名称与备注(调用大模型,超时放宽) */ export function parseRelationByAi(data: { type: string members: { type: string; channel?: string; categoryName?: string; remark?: string }[] }) { return request.post('/ai/relation', data, { timeout: 60000 }) } /** 根据表单当前内容生成单条分类与标签建议(调用大模型,超时放宽) */ export function suggestOneByAi(data: { type: string; amount?: number | null; channel?: string; remark?: string }) { return request.post('/ai/suggest-one', data, { timeout: 60000 }) } /** AI 推荐分类 Emoji 图标(调用大模型,超时放宽) */ export function suggestEmoji(name: string, type?: string) { return request.post('/ai/emoji', { name, type }, { timeout: 60000 }) } /** AI 推荐标签颜色(调用大模型,超时放宽) */ export function suggestColor(name: string) { return request.post('/ai/color', { name }, { timeout: 60000 }) } /** AI 生成的规则建议 */ export interface AiRuleSuggestion { name: string type: string | null keyword: string | null minAmount: number | null maxAmount: number | null categoryId: number | null categoryName: string | null newCategoryName: string | null newCategoryIcon: string | null tagIds: number[] tagNames: string[] newTags: AiNewTag[] reason: string /** 是否与已有规则重复:重复时默认不勾选且禁止导入 */ duplicate: boolean } /** AI 生成规则的结果 */ export interface AiRuleSuggestResult { summary: string rules: AiRuleSuggestion[] } /** AI 统计查询解析结果 */ export interface AiStatisticsResult { summary: string types: string[] categoryIds: number[] categoryNames: string[] /** 是否仅筛选未分类记录 */ uncategorized: boolean tagIds: number[] tagNames: string[] /** 是否仅筛选无标签记录;与 tagIds 互斥 */ untagged: boolean channels: string[] startTime: string | null endTime: string | null minAmount: number | null maxAmount: number | null remark: string | null statsScope: 'INCLUDED' | 'ALL' | 'EXCLUDED' | null dimension: 'day' | 'week' | 'month' | 'quarter' | 'year' | null unmatched: string[] } /** 将自然语言统计查询解析为统计面板参数(调用大模型,超时放宽) */ export function parseStatisticsByAi(query: string) { return request.post('/ai/statistics', { query }, { timeout: 120000 }) } /** 根据选中的已打标签记录生成规则建议(调用大模型,超时放宽) */ export function suggestRulesByAi(transactionIds: number[]) { return request.post('/ai/rule/suggest', { transactionIds }, { timeout: 120000 }) } /** 批量创建 AI 生成的规则,必要时新建分类与标签 */ export function applyAiRules(rules: { name: string enabled?: boolean type?: string | null keyword?: string | null minAmount?: number | null maxAmount?: number | null categoryId?: number | null newCategoryName?: string | null newCategoryIcon?: string | null tagIds?: number[] newTags?: AiNewTag[] }[]) { return request.post('/ai/rule/apply', { rules }) } /** 一键应用建议:覆盖分类并追加标签,标记为新建的分类/标签会先创建再关联 */ export function applyAiSuggestions(suggestions: { transactionId: number categoryId: number | null newCategoryName?: string | null newCategoryIcon?: string | null tagIds: number[] newTags?: AiNewTag[] }[]) { return request.post('/ai/apply', { suggestions }) } /** 自然语言查询解析结果 */ export interface AiQueryResult { summary: string types: string[] categoryIds: number[] categoryNames: string[] uncategorized: boolean channels: string[] startTime: string | null endTime: string | null includeInStats: boolean | null minAmount: number | null maxAmount: number | null remark: string | null tagIds: number[] tagNames: string[] /** 是否筛选无标签记录;与 tagIds 互斥 */ untagged: boolean unmatched: string[] } /** 将自然语言查询解析为筛选条件 */ export function queryByAi(query: string) { return request.post('/ai/query', { query }, { timeout: 120000 }) } /** 智能查数结果的列元数据 */ export interface AiSqlColumn { key: string title: string type: 'NUMBER' | 'STRING' | 'DATE' | 'DATETIME' | 'BOOLEAN' } /** 智能查数结果 */ export interface AiSqlQueryResult { /** 生成的 SQL(经安全校验后的最终语句) */ sql: string /** 查询意图说明 */ explanation: string /** 建议的展示类型 */ chartType: 'SCALAR' | 'TABLE' | 'BAR' | 'PIE' /** 结果列元数据 */ columns: AiSqlColumn[] /** 结果行数据 */ rows: Record[] /** 返回行数 */ rowCount: number /** 是否因行数上限被截断 */ truncated: boolean } /** 将自然语言转换为只读 SQL 并执行查询(调用大模型,超时放宽) */ export function queryBySql(query: string) { return request.post('/ai/sql', { query }, { timeout: 120000 }) } /** 智能查数历史记录 */ export interface AiSqlHistoryItem { id: number query: string createdAt: string } /** 获取智能查数历史(按创建时间倒序) */ export function getSqlHistory(limit = 100) { return request.get('/ai/sql/history', { params: { limit } }) } /** 清空智能查数历史 */ export function clearSqlHistory() { return request.delete('/ai/sql/history') } /** 自然语言记账解析结果 */ export interface AiRecordResult { summary: string type: string amount: number | null categoryId: number | null categoryName: string | null channel: string | null transactionTime: string | null remark: string | null includeInStats: boolean | null tagIds: number[] tagNames: string[] unmatched: string[] /** 图片记账时由模型提取的原始字段 JSON 文本,文本记账为 null */ rawData: string | null /** 来源图片序号(图片记账,从 1 开始),文本记账为 null */ imageIndex: number | null /** 来源图片ID(图片记账),文本记账为 null */ imageId: number | null /** 是否疑似重复:与已有记录或本批其它记录的类型、金额、时间一致 */ duplicate: boolean | null } /** 将自然语言记账内容解析为交易记录 */ export function parseRecordByAi(text: string) { return request.post('/ai/record', { text }, { timeout: 120000 }) } /** 图片记账解析结果 */ export interface AiRecordImageResult { /** 保存的图片ID列表,按上传顺序排列 */ imageIds: number[] summary: string records: AiRecordResult[] unmatched: string[] } /** 上传一至多张账单/支付截图,由 AI 一次性识别并生成多条交易记录(调用视觉模型,超时放宽) */ export function parseRecordByImage(files: File[], hint?: string) { const formData = new FormData() for (const file of files) { formData.append('files', file) } if (hint) formData.append('hint', hint) return request.post('/ai/record/image', formData, { timeout: 180000 }) } /** 获取 AI 记账图片(blob,经鉴权头请求,避免 token 出现在 URL;thumbnail=true 返回压缩缩略图,默认原图) */ export function getAiImageBlob(id: number, thumbnail = false) { return request.get(`/ai/image/${id}${thumbnail ? '?thumbnail=true' : ''}`, { responseType: 'blob', timeout: 30000 }) } /** 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 } }) }