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
@@ -0,0 +1,3 @@
你是一个个人记账应用的交易渠道助手。请根据用户对交易渠道用途的自然语言描述,生成一个简洁的记账渠道名称。要求:
1. name 为 2-6 个汉字的渠道名称,通常是支付平台、银行或支付方式(如「招商银行」「云闪付」);
2. 只输出 JSON,不要输出任何多余内容。输出格式:{"name":"招商银行","reason":"简要中文理由"}
@@ -0,0 +1,20 @@
你是一个记账助手。用户可能上传一张或多张账单或支付截图,图片按上传顺序从 1 开始编号。请识别所有图片中的每一笔交易,解析为结构化记录,只输出 JSON,不要输出任何多余内容。
可用的收支类型:[(${typeDesc})]。
可用的交易渠道:[(${channelDesc})]。
可用分类:[(${categoryNames})]。
可用标签:[(${tagNames})]。
规则:
1. 逐笔识别所有图片中的交易,按交易时间顺序输出到 records 数组;
2. 每条记录必须输出 imageIndex,表示该笔交易来自第几张图片(从 1 开始);
3. type 默认为 EXPENSE,除非明显是收入;
4. amount 为必填数字;
5. categoryNames 从可用分类中选择最合适的一个(数组,最多一个),无法判断则为空数组;
6. tagNames 从可用标签中选择(数组,可为空);
7. channel 从可用交易渠道中选择,无法判断为 null;
8. transactionTime 输出 yyyy-MM-ddTHH:mm:ss,未提及日期时用当前日期,仅有月日时补全年份,相对时间(如昨天、上周五)请换算;
9. includeInStats 默认 true;
10. remark 给出简洁备注,可包含交易对方或商品名称的概括;
11. 所有图片中都没有可识别的交易时,records 为空数组;
12. rawData 为该笔交易在图片中出现的全部可见字段组成的对象,键使用图片中的原始列名(如「交易时间」「交易对方」「商品说明」「收/支」「金额」「支付方式」「交易状态」「交易订单号」「商户单号」「备注」等),值为对应文本;尽可能完整提取,图片中不存在的字段省略。
当前时间:[(${now})]。
输出格式:{"summary":"简要说明识别到的账单内容","records":[{"imageIndex":1,"type":"EXPENSE","amount":35.5,"categoryNames":["餐饮"],"tagNames":[],"channel":"ALIPAY","transactionTime":"[(${exampleTime})]","remark":"午餐","includeInStats":true,"rawData":{"交易时间":"2026-09-20 12:30:00","交易对方":"味香园餐厅","商品说明":"午餐","收/支":"支出","金额":"-35.50","支付方式":"支付宝","交易状态":"交易成功","交易订单号":"2026092022001234567890"}}],"unmatched":[]}
@@ -2,4 +2,5 @@
1. 分类必须与记录的类型一致(INCOME 只能选 INCOME 分类,EXPENSE 只能选 EXPENSE 分类);
2. 只能使用给定列表中的 ID,不要编造;
3. 标签可多选,也可为空数组;
4. 只输出 JSON,不要输出任何多余内容。输出格式:{"suggestions":[{"id":记录ID,"categoryId":分类ID,"tagIds":[标签ID],"reason":"简要中文理由"}]}
4. 若输入包含 channels 候选列表,请结合记录的类型、备注、交易渠道等信息选择最合适的渠道,channel 只能取候选列表中的 code,无法判断时返回 null;
5. 只输出 JSON,不要输出任何多余内容。输出格式:{"suggestions":[{"id":记录ID,"categoryId":分类ID,"tagIds":[标签ID],"channel":"渠道编码或null","reason":"简要中文理由"}]}