一、AI 智能票据识别
接入大视觉模型(默认 DashScope qwen-vl-max,可自定义 API Base 和模型)
自动识别票据类型(机票、火车票、船票、汽车票、网约车、其他)
自动提取金额、日期、行程、车次、姓名等结构化字段
支持批量上传,异步识别并实时显示进度
二、多格式票据上传
PDF:电子发票、行程单、火车票 PDF
图片:PNG、JPG、JPEG、GIF、BMP、WEBP(纸质发票拍照、截图)
邮箱收取:配置 IMAP 邮箱后,一键从邮件附件批量拉取电子发票
三、自动去重与张数智能计算
行程单 + 电子发票配对:当两者金额相等时自动识别为一组
行程单:计张数,不计金额
电子发票:计金额,不计张数
trip_count 支持:一份行程单含多条子行程(如 15 次打车),张数自动按子行程数量累计
去重后金额、张数在界面上实时标注,一目了然
四、模板管理
支持上传 Word(.docx)和 Excel(.xlsx)模板
自动识别模板内占位符(类别、金额、票据张数等)
可视化配置字段映射,按需填入对应单元格
合并单元格完美保留:生成及预览时不打散、不重复
五、报销单生成
选择日期范围、模板、已完成识别的票据
系统按类别(机票/车船费/市内车费/住宿费/其他)自动汇总金额和张数
实时预览生成结果(Word/Excel 转 HTML 内嵌预览)
一键生成 .docx 或 .xlsx 文件,大写金额自动填充
六、下载与分发
单文件下载:Word 或 Excel 报销单
ZIP 打包下载:报销单 + 所有票据 PDF/图片(分类到"票据附件"子目录)
中文文件名,无多余 manifest 等文件
七、工作台与历史记录
所有票据、模板、报销单本地保存,方便追溯
支持按状态、类型、日期筛选
重新识别、删除、查看详情等操作完整可用
技术实现说明
这个系统本质上是一个「桌面壳包着一个小型本地 Web 服务」的应用。用户双击桌面上的快捷方式后,Electron 会先在后台静默启动一个 Python 写的小型 Web 服务器(端口 8900),然后弹出一个浏览器窗口去访问这个本地服务器,所以用户看起来就是在用一个普通的桌面软件,但底层其实跑的是前后端分离的 Web 架构。
前端部分
界面是用 Vue 3 写的,样式走 Element Plus 组件库。整个前端按三步向导的思路组织:第一步配置大模型和邮箱,第二步上传票据并等 AI 识别完成,第三步选模板和日期范围生成报销单并预览下载。Vite 打包的时候把资源路径改成了相对路径,这样不管是在开发环境用本地 vite 服务器,还是打包后由 Python 后端静态托管,页面里的 JS、CSS、图片都能正确加载。
后端部分
后端用的是 FastAPI,跑在 uvicorn 异步服务器上,全程不阻塞。数据库选了 SQLite,文件就一个 app.db,不需要用户单独装数据库服务。访问数据库用的是 SQLAlchemy 2.0 的异步模式,配合 aiosqlite 驱动,保证高并发下不卡。整个数据库一共就四张表:票据、模板、报销单、邮箱配置,票据识别出来的结构化字段因为结构不固定,所以存成 JSON 字段塞进去,省事又灵活。
AI 识别票据
拿到文件后先判断格式:如果是 PDF,就用 PyMuPDF 把每一页高清转成 PNG 图片;如果本来就是图片(PNG、JPG、WEBP 这些都支持),就压缩一下尺寸。然后把图片按「data:image/xxx;base64,」的格式拼好,调用兼容 OpenAI 视觉接口的大模型(默认是阿里云 DashScope 的 qwen-vl-max,用户也可以自己改 API 地址和模型名)。Prompt 里严格要求模型输出 JSON 结构,包括票据类型、金额、日期、各项明细;对于网约车行程单,还特别要求模型识别出来里面一共包含多少条子行程,这个数字叫 trip_count。由于模型偶尔会在 JSON 外面套 ```json 代码块,所以在解析的时候做了一层兜底:先尝试直接 parse JSON,失败就用正则把 code fence 扒掉再 parse,保证不会因为格式问题白忙活。