feat: AI 图片记账、自定义交易渠道与淘宝/京东账单导入

- AI 图片记账:多图上传由视觉模型识别,SSE 实时推送进度,提取 rawData,按「类型+金额+时间」检测重复,支持缩略图与来源图片查看
- 自定义交易渠道:新增 t_channel 与渠道管理页,支持新建/改名/删除,并接入统计、导出与 AI 查询/记账/建议提示词
- AI 建议:单条建议新增交易渠道推荐(推荐已有渠道 / AI 新建渠道)
- 账单导入:新增淘宝(xlsx)、京东(CSV)解析器
- 交易:新增批量创建接口,导入时按去重键兜底跳过重复
- 运行目录统一经 AppHome 解析,图片与数据文件落盘规范化
- 前端:AI 记账方式偏好记忆、AI 按钮与输入框对齐、来源图片弹窗支持加载原图
This commit is contained in:
2026-09-21 16:21:58 +08:00
parent 96130d269e
commit 2ade60da42
73 changed files with 5024 additions and 354 deletions
+47 -1
View File
@@ -8,6 +8,7 @@ export interface AiConfig {
hasApiKey: boolean
baseUrl: string
model: string
visionModel: string
}
/** AI 建议新建的标签 */
@@ -27,6 +28,8 @@ export interface AiSuggestion {
newCategoryIcon: string | null
tagIds: number[]
tagNames: string[]
channel: string | null
channelName: string | null
newTags: AiNewTag[]
reason: string
}
@@ -37,7 +40,7 @@ export function getAiConfig() {
}
/** 保存 AI 配置,apiKey 留空表示不修改原值 */
export function saveAiConfig(data: { enabled: boolean; apiKey?: string; baseUrl?: string; model?: string }) {
export function saveAiConfig(data: { enabled: boolean; apiKey?: string; baseUrl?: string; model?: string; visionModel?: string }) {
return request.put('/ai/config', data)
}
@@ -77,6 +80,17 @@ 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 })
}
/** 根据表单当前内容生成单条分类与标签建议(调用大模型,超时放宽) */
export function suggestOneByAi(data: { type: string; amount?: number | null; channel?: string; remark?: string }) {
return request.post('/ai/suggest-one', data, { timeout: 60000 })
@@ -213,6 +227,14 @@ export interface AiRecordResult {
tagIds: number[]
tagNames: string[]
unmatched: string[]
/** 图片记账时由模型提取的原始字段 JSON 文本,文本记账为 null */
rawData: string | null
/** 来源图片序号(图片记账,从 1 开始),文本记账为 null */
imageIndex: number | null
/** 来源图片ID(图片记账),文本记账为 null */
imageId: number | null
/** 是否疑似重复:与已有记录或本批其它记录的类型、金额、时间一致 */
duplicate: boolean | null
}
/** 将自然语言记账内容解析为交易记录 */
@@ -220,6 +242,30 @@ 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