docs: 清理过期计划文档并补全 work-log 与开发/用户文档
- 删除已落地的 improvement-roadmap、superpowers 计划与 ui-mockups 参考稿,删除早期 work-log(2026-05-06~08) - 新增 2026-05-23 起的 work-log 与 attachment-association-background-job、reimbursement-draft-action-branching 等开发文档及用户文档 - docker-compose(.full).yml 微调服务配置
This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
# 附件自动关联后台任务方案
|
||||
|
||||
## 背景
|
||||
|
||||
小财管家 AI 模式里,用户上传票据后会先做 OCR。当前附件自动关联报销草稿的后半段仍依赖前端会话内存:
|
||||
|
||||
- 前端保留浏览器里的 `File` 对象。
|
||||
- 前端在当前会话里完成草稿匹配。
|
||||
- 用户点击“确认自动关联”后,再用这些 `File` 上传到报销单明细。
|
||||
|
||||
这会带来一个核心问题:用户发送消息后,如果刷新页面、切换会话或退出当前会话,浏览器内存里的附件原件会丢失,历史消息中的关联动作就无法继续执行。
|
||||
|
||||
票据夹已经能持久化 OCR 结果和源文件,并且 OCR 返回里已经带有 `receipt_id`。所以正式方案应该把“附件关联”从前端内存任务调整为后端可查询任务。
|
||||
|
||||
## 目标
|
||||
|
||||
1. 用户一上传附件即触发 OCR,附件卡片显示“识别中”,识别完成后显示“当前附件已识别”。
|
||||
2. 用户点击发送后,前端不再依赖 `File` 原件做后续归集,而是把 OCR 产生的 `receipt_id` 提交给后端。
|
||||
3. 后端创建附件关联任务,并在后台继续匹配、复制票据夹源文件、关联报销单明细。
|
||||
4. 用户退出或刷新当前会话后,前端可通过保存下来的 `job_id` 查询任务状态并更新消息。
|
||||
5. 如果匹配失败、置信度不足或找不到可编辑草稿,系统给出清晰提示,引导用户补充说明、上传附件或新建草稿。
|
||||
|
||||
## 非目标
|
||||
|
||||
第一版不承诺后端服务进程重启后任务状态仍可恢复。
|
||||
|
||||
本次实现使用轻量内存任务池,解决“用户离开前端会话导致连接断开”的问题。后续如果要做到服务重启、横向扩容、任务审计都可恢复,再新增数据库任务表。
|
||||
|
||||
## 核心流程
|
||||
|
||||
```text
|
||||
用户上传票据
|
||||
→ 前端立即调用 OCR
|
||||
→ OCR 写入票据夹并返回 receipt_id
|
||||
→ 附件卡片显示“当前附件已识别”
|
||||
→ 用户点击发送
|
||||
→ 前端 POST 创建附件关联任务
|
||||
→ 后端后台任务继续运行
|
||||
→ 前端轮询 job_id
|
||||
→ 成功:消息显示已关联,并刷新报销单详情
|
||||
→ 失败:消息显示失败原因和下一步动作
|
||||
```
|
||||
|
||||
## 后端设计
|
||||
|
||||
### API
|
||||
|
||||
新增接口:
|
||||
|
||||
- `POST /api/v1/reimbursements/attachment-association-jobs`
|
||||
- `GET /api/v1/reimbursements/attachment-association-jobs/{job_id}`
|
||||
|
||||
创建任务请求只传持久化引用,不传浏览器文件:
|
||||
|
||||
```json
|
||||
{
|
||||
"receipt_ids": ["receipt-1", "receipt-2"],
|
||||
"prompt": "请帮我处理已上传的附件。",
|
||||
"conversation_id": "inline-xxx"
|
||||
}
|
||||
```
|
||||
|
||||
状态返回:
|
||||
|
||||
```json
|
||||
{
|
||||
"job_id": "job-xxx",
|
||||
"status": "running",
|
||||
"message": "正在匹配可关联的报销草稿...",
|
||||
"receipt_ids": ["receipt-1", "receipt-2"],
|
||||
"claim_id": "",
|
||||
"claim_no": "",
|
||||
"uploaded_count": 0,
|
||||
"skipped_count": 0,
|
||||
"error": ""
|
||||
}
|
||||
```
|
||||
|
||||
状态枚举:
|
||||
|
||||
- `queued`:任务已创建,等待后台执行。
|
||||
- `running`:正在匹配和归集。
|
||||
- `succeeded`:已完成自动归集。
|
||||
- `failed`:无法自动完成,返回可读错误。
|
||||
|
||||
### 任务执行
|
||||
|
||||
后端任务执行步骤:
|
||||
|
||||
1. 按 `receipt_ids` 读取票据夹明细和源文件。
|
||||
2. 从票据 OCR 文本、结构化字段、日期、城市提取匹配信号。
|
||||
3. 查询当前用户可见且可编辑的报销草稿,排除申请单和已归档单据。
|
||||
4. 对候选草稿按日期、城市、事由、草稿状态评分。
|
||||
5. 置信度足够时选择目标草稿。
|
||||
6. 为每份票据找到可用费用明细,没有合适明细时创建空明细。
|
||||
7. 从票据夹源文件复制到报销单附件目录,并沿用 `source_receipt_id` 回填 OCR 信息。
|
||||
8. 更新票据夹状态为 `linked`。
|
||||
9. 写入任务最终状态。
|
||||
|
||||
## 前端设计
|
||||
|
||||
### 会话消息
|
||||
|
||||
AI 消息需要持久化 `attachmentAssociationJob`:
|
||||
|
||||
```json
|
||||
{
|
||||
"jobId": "job-xxx",
|
||||
"status": "running",
|
||||
"receiptIds": ["receipt-1", "receipt-2"]
|
||||
}
|
||||
```
|
||||
|
||||
这样历史会话恢复后,不再需要浏览器里的 `File` 对象,只要消息里仍有 `jobId`,就可以继续查询后端状态。
|
||||
|
||||
### 发送行为
|
||||
|
||||
当用户上传附件并点击发送:
|
||||
|
||||
1. 前端确认 OCR 已完成。
|
||||
2. 从 OCR 结果里提取 `receipt_id`。
|
||||
3. 创建后端任务。
|
||||
4. 立即持久化带 `job_id` 的 pending 消息。
|
||||
5. 页面仍打开时轮询任务状态。
|
||||
6. 页面恢复或打开历史会话时,对未完成任务继续轮询。
|
||||
|
||||
## 异常处理
|
||||
|
||||
- 未完成 OCR:禁止发送,提示“附件 OCR 识别中,请稍等,识别完成后再继续对话。”
|
||||
- OCR 失败:禁止发送,提示“请先移除识别失败的附件或重新上传。”
|
||||
- 没有 `receipt_id`:提示“当前附件没有持久化票据记录,请重新上传后再试。”
|
||||
- 没有可编辑草稿:任务失败,提示用户先新建或选择草稿。
|
||||
- 多个候选置信度接近:第一版不自动归集,提示补充说明或手动选择。
|
||||
- 服务重启导致内存任务丢失:返回任务不存在,前端提示“后台任务状态已失效,请重新发送附件关联请求。”
|
||||
|
||||
## 验收标准
|
||||
|
||||
1. 上传附件后必须先进入 OCR 识别中状态,识别完成前不能发送对话。
|
||||
2. 发送附件关联请求后,前端能收到并保存 `job_id`。
|
||||
3. 用户离开当前会话后,后端任务仍会继续执行。
|
||||
4. 用户回到历史会话后,前端可以根据 `job_id` 查询并更新最终状态。
|
||||
5. 成功后报销单明细出现附件,票据夹状态变为已关联。
|
||||
6. 找不到草稿、低置信度、源文件缺失时,消息能给出明确原因。
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
# 附件自动关联后台任务 TODO
|
||||
|
||||
- [x] 梳理当前前端内存任务断链原因。
|
||||
- [x] 明确第一版边界:页面/会话退出可恢复,服务进程重启暂不承诺恢复。
|
||||
- [x] 新增后端任务 schema。
|
||||
- [x] 新增后端后台任务服务。
|
||||
- [x] 新增任务创建和查询接口。
|
||||
- [x] 补后端任务测试。
|
||||
- [x] 新增前端任务 service。
|
||||
- [x] 调整 AI 模式附件关联流程为创建后端任务。
|
||||
- [x] 持久化消息里的 `attachmentAssociationJob`。
|
||||
- [x] 历史会话恢复时继续轮询未完成任务。
|
||||
- [x] 更新前端源码断言测试。
|
||||
- [x] 容器内运行后端定向测试。
|
||||
- [x] 运行前端定向测试和构建。
|
||||
|
||||
## 验证记录
|
||||
|
||||
- `docker exec -w /app -e SERVER_VENV_DIR=/tmp/x-financial-server-venv x-financial-local-linux /tmp/x-financial-server-venv/bin/pytest -q server/tests/test_attachment_association_jobs.py server/tests/test_ocr_endpoints.py server/tests/test_reimbursement_endpoints.py::test_claim_item_attachment_upload_preview_and_delete server/tests/test_openapi_schema.py`
|
||||
- `node --test web/tests/workbench-ai-mode-switch.test.mjs web/tests/ai-attachment-association-model.test.mjs`
|
||||
- `npm --prefix web run build`
|
||||
- 已重启 `x-financial-local-linux`,并确认 `/api/v1/reimbursements/attachment-association-jobs/not-exist` 返回自定义任务失效提示。
|
||||
Reference in New Issue
Block a user