Files
group_fqcd_jr/to_do_list.md
T

107 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 场外基金申购赎回后续任务清单
> 更新方式:完成一项实时打勾,并补充验证结果。
> 范围:场外基金申购赎回独立接口、独立表、真实能力配置入口和可回归测试。
- [x] 1. 阅读项目规范、现有代码、调用链、数据流、权限和影响面。
备注:已阅读根目录与子项目 AGENTS、CLAUDE、AI 阅读/执行/测试/输出规范、MVC 规范、Agent 接入说明、数据库基线、建表设计、接口文档、NL2SQL 接入说明、场外流程图和本 SKILL。
- [x] 2. 明确本次需求并拆分邮件、识别、核对、统计、通知、审计任务。
备注:本轮按用户确认分两阶段推进:先做当前业务闭环兼容完善,再按上线方案接入真实 IMAP/OCR/SMTP;不改变现有成功链路和接口路径。
- [x] 3. 完善邮件接收、补偿、幂等、编号和原始文件保存。
备注:新增独立 `OffsiteMailWorker`,按 INBOX UID 增量扫描,持久化游标、失败 UID、重试时间和租约;成功邮件才推进游标,失败邮件停留并可重启补偿。新增可配置 IMAP IDLE 等待,超时、断线和事件后均回到 UID 补偿扫描。
- [x] 4. 完善附件分类、OCR/LLM 一次识别和识别异常处理。
备注:保持 summary 拆分、other 归档;新增字段缺失、低置信识别状态分流,不覆盖原始识别字段。
- [x] 5. 完善 NL2SQL、Plan and Execute 和查询失败隔离。
备注:恢复并复用 `nl2sql_yc.query_dict`;新增场外专用适配器隔离调用参数和副作用;触发查询后独立保存每条规则查询记录并回填查询阶段计划状态。
- [x] 6. 完善确定性计算、申购规则和赎回规则。
备注:继续使用确定性规则引擎;已覆盖申购金额 0.99/1.00/1.01 元、20% 比例边界、赎回可用份额等于和超出边界。
- [x] 7. 完善人工确认、汇总统计、邮件返回和独立通知路由。
备注:新增正常返回/异常返回类型并保留旧 `mail_return` 兼容;统计只纳入确认正常且正常回复发送成功的单据;补充混合邮件分流、邮件级完成状态和正常净值查询结果解析。
- [x] 8. 完善配置、权限、日志、重试、审计和数据安全。
备注:场外写接口在 Service 层增加角色和权限二次校验;人工确认、NL2SQL 触发、通知创建写入 `interaction_audit`;通知 payload 对账户标识脱敏。
- [x] 9. 新增真实 IMAP 收信适配层,默认关闭并支持健康检查、自动重连和增量补偿扫描。
备注:新增 `OffsiteImapReceiver`,默认由 `OFFSITE_IMAP_ENABLED=false` 关闭;支持健康检查、缺配置识别、只选 `INBOX`、按 UID 增量扫描、可配置 IDLE、白名单发件人过滤和连接异常后下一轮重连。新增 Worker 安全前置:真实 IMAP 场景未同时启用且健康通过真实 OCR/DeepSeek 时,拒绝业务处理,防止 Mock 结果入库;真实邮箱联调仍需按灰度方案人工执行。
- [x] 10. 新增原始 MIME 邮件和附件受控保存流程,保留 UID、Message-ID、附件哈希、路径、失败原因和重试记录。
备注:新增 `OffsiteMailStorage` 保存原始 `.eml` 与附件;附件路径包含 SHA256 前缀,避免同名不同内容覆盖;Worker 失败时持久化游标和邮件级错误/重试字段,并保留原始文件。真实对象存储、容量治理、备份和保留期限仍需上线前配置。
- [x] 11. 新增阿里云 OCR/Document AI 与 DeepSeek 识别适配层,默认 Mock,可配置真实调用、超时、降级和脱敏日志。
备注:已接入阿里云官方 `alibabacloud_docmind_api20220711==1.4.14` SDK,按“提交文档解析任务→轮询状态→读取版面结果”流程处理本地附件;DeepSeek 使用 OpenAI 兼容 `/chat/completions` 进行分类和字段 JSON 映射。已补充 SDK 缺失健康检查、请求模型校验、轮询超时、文件流关闭、结构化字段、字段置信度、缺失字段和低置信字段输出。真实 OCR/DeepSeek 网络调用仍保持开关关闭,待受控单附件灰度。
- [x] 12. 新增 SMTP 邮件发送适配层,默认 dry-run,发送前必须运营确认,记录发送状态、provider message ID、失败原因和重试次数。
备注:新增 `OffsiteSmtpSender`,默认 `OFFSITE_SMTP_ENABLED=false` 且 `OFFSITE_SMTP_DRY_RUN=true`;发送前强制检查 `operator_confirmed`,支持回复原邮件、重新附加原附件、返回发送状态、失败原因和重试次数。新增发送中超时接管为“发送失败 + 人工核验”,不自动判断外部是否已投递;真实 SMTP 灰度尚未执行。
- [x] 13. 为真实外部服务补齐 Mock 单元测试、集成测试、灰度验证步骤和上线回滚方案。
备注:新增 IMAP/原始文件保存、OCR/DeepSeek 识别、SMTP dry-run/真实发送 Mock 单元测试;新增通知成功幂等、失败状态回写和正常回复完成状态集成测试;新增 `docs/场外外部服务灰度与回滚方案.md`。官方阿里云 SDK 已安装并完成本地模型/健康检查验证;真实 IMAP/OCR/DeepSeek/SMTP 灰度尚未执行,且本次真实 IMAP 访问受运行环境网络审批阻断。
- [x] 14. 执行代码检查和自动化验证。
备注:全量 `pytest` 206 passed;场外外部服务相关回归 28 passed;`ruff`、`mypy` 和 `git diff --check` 均通过。集成测试已通过 `tests/integration/conftest.py` 与真实 `.env` 隔离,避免生产邮箱角色配置污染自动化回归。
- [x] 15. 再次阅读本 SKILL,核查是否有遗漏的内容和任务未生成到清单执行。
备注:已再次阅读场外基金 SKILL;真实 IMAP/OCR/SMTP 联调、生产密钥、存储备份、监控和灰度验收仍属于上线前人工任务,未伪装为代码完成。
- [x] 16. 对功能和 BUG 进行测试。
备注:新增并通过 IMAP IDLE 事件/超时/断线、Worker IDLE 后 UID 补偿、真实 IMAP 场景拒绝 Mock 识别、统计未发送不计入、成功发送后计入、正常/异常返回分流和混合邮件状态测试;全量回归 191 passed。
- [x] 17. 执行真实 IMAP 单附件联调并核验阿里云 DocMind/DeepSeek 外部调用。
备注:首次联调因 DocMind 未开通返回 `DocMindServiceNotOpen`;开通后已成功读取 UID `1785837410` 的 1 个真实附件,阿里云 OCR 与 DeepSeek 均返回成功。本次未写入数据库、未启动 Worker、未发送 SMTP。
- [x] 18. 完善真实模型输出兼容和单附件识别安全闭环。
备注:修复 OCR 失败仍继续调用 DeepSeek 的问题;兼容 DeepSeek 将已知业务字段返回在顶层的情况;补充固定 JSON 提示词、未知字段过滤、当前单据类型缺失字段过滤和完整必填字段回算。真实赎回单最终提取 8 个核心字段、当前类型缺失字段 0 个,账户标识和代销机构低置信,按规则进入人工复核。
- [x] 19. 确认真实申购 Word 文档是否需要重复外部联调。
备注:用户确认申购单与赎回单在本次真实外部识别验证范围内视为同类文档,已完成的真实赎回 Word 文档联调作为代表样本,申购 Word 文档不再重复执行真实 IMAP/OCR/DeepSeek 联调。申购专属字段、规则分支和业务流程仍由现有自动化测试覆盖;该决定不代表申购真实样本已单独联调。
- [x] 20. 完成真实申购单识别结果人工复核。
备注:真实申购邮件 UID `1785837411` 的 Word 附件已完成 IMAP、阿里云 DocMind、DeepSeek 联调;识别类型为 `subscription`,提取 9 个字段,缺失字段 0 个,1 个低置信字段经用户确认无误。当前仍未启动 Worker、未发送 SMTP、未写入业务数据库。
- [x] 21. 生成场外 Worker 最小权限技术账号迁移和只读核验脚本。
备注:新增幂等 Alembic 迁移,创建/复用 `operator`、`offsite:write` 和专用 `offsite_worker` 技术账号;发现既有 operator 额外权限或其它用户会被扩大权限时自动中止。新增只读核验脚本和迁移安全测试。
- [x] 22. 再次阅读本 SKILL,核查是否有遗漏的内容和任务未生成到清单执行。
备注:已复核真实邮箱、真实 OCR/DeepSeek、Worker 身份、迁移安全边界和 SMTP 关闭约束;迁移执行前人工确认已完成。
- [x] 23. 对功能和 BUG 进行测试。
备注:新增迁移安全测试 4 项并通过;全量 pytest `214 passed`,ruff、mypy、权威文档检查、数据库结构审计、Alembic head 和 `git diff --check` 均通过。
- [x] 24. 执行场外 Worker 技术账号迁移并完成数据库核验。
备注:已执行 `alembic upgrade head`,数据库版本为 `20260910_offsite_worker_identity`;已核验 `offsite_worker` 用户、`operator` 角色、`offsite:write` 权限和迁移审计记录。用户 ID 为 `1150264986594785`。
- [x] 25. 新增识别尝试持久化与自动重试恢复。
备注:新增 `offsite_recognition_attempt` 独立记录表;自动识别最多重试一次,记录来源、序号、OCR/DeepSeek 状态、字段结果、缺失/低置信字段和错误,不覆盖原始附件识别结果。
- [x] 26. 新增识别异常单据人工重试兼容入口。
备注:新增 `POST /api/v1/offsite-fund/documents/{task_id}/recognition-retries`;成功重试只恢复当前单据字段、规则结果和执行计划,失败保留 `recognition_exception`,所有动作写入审计。
- [ ] 27. 对现有申购异常任务执行真实识别重试并验证后续闭环。
备注:已新增 `../seed_offsite_test_data.py`,使用 `T_OFFSITE_0910` 标记插入可删除的临时产品、行情、净值和持仓数据。重新核对后,`20260910-001-A01` 的两条 `nl2sql_yc.query_dict` 查询均返回 1 行,三条申购规则均为“正常”,单据状态为 `planned`,当前保留临时数据等待前端核对;运营确认、通知和统计仍未执行。
- [x] 28. 再次阅读本 SKILL,核查真实 Worker 灰度结果和上线缺口。
备注:已复核真实 IMAP、OCR、DeepSeek、场外独立入库、游标、权限和 SMTP 关闭约束。确认当前代码对首次不完整识别结果会安全分流为 `recognition_exception`,并已补充“保留原始识别值的受控识别重试/恢复入口”。
- [✔] 29. 对功能和 BUG 进行测试。
备注:场外定向回归 10 passed;全量 pytest 225 passed;mypy、权威文档检查和数据库结构审计通过。真实申购单已验证查询失败隔离、规则无法判断和业务确认阻断;真实业务闭环仍受金融基础数据为空阻塞。
- [x] 30. 创建本地测试运营账号并生成前端联调 JWT。
备注:`../seed_offsite_test_data.py` 复用已有 `operator` 角色和 `offsite:write` 权限,新增独立 `operator`、`token`、`verify` 和清理逻辑;JWT 仅使用本地私钥生成短时令牌,不新增正式登录接口、不修改共享角色。测试账号已在本地数据库创建并完成 `JwtAuthenticator`、`IdentityService` 链路核验。
- [x] 31. 新增邮件 OCR 识别字段只读查询接口并接入前端。
备注:新增 `GET /api/v1/offsite-fund/mails/{mail_id}/recognition-fields`(归属场外业务域,权限码沿用 `offsite:read`,幂等范围天然只读、无幂等键,事务边界为单次只读事务,审计类别 `offsite.mail_recognition_viewed`,Agent 边界为只读)。返回识别原文、结构化字段、字段置信度、缺失/低置信字段、最近一次识别尝试和单据标准化字段;前端点击邮件即展示“OCR 识别字段”面板,并把识别值合并进单据卡片。集成测试 4 项通过,浏览器实测通过。
- [x] 32. 前端接入附件原件预览接口。
备注:`GET /api/v1/offsite-fund/attachments/{attachment_id}/file` 后端实现与集成测试已具备;前端新增带 JWT 的二进制读取、预览浮层(PDF 与图片内联、其它类型降级为下载)、对象 URL 释放和 401 回退。跨域下浏览器不允许读取 `Content-Disposition`,改为按后端返回内容类型判断是否内联。PDF 内联预览与 Office 类型降级提示已在浏览器实测通过。
- [x] 33. 修复前端本地静态服务缓存导致的模块加载报错。
备注:故障现象为 `does not provide an export named 'fetchFileBlob'`,根因是 `python -m http.server` 不发送缓存响应头,浏览器按启发式规则缓存了旧 `client.js`。新增 `前端/serve.py` 静态服务,对本地联调资源统一返回 `Cache-Control: no-store`,并同步更新 `package.json` 的 `dev`/`start` 脚本;服务端文件核实为最新版本、页面加载无控制台错误。
- [x] 34. 新增 NL2SQL 返回字段只读查询接口。
备注:新增 `GET /api/v1/offsite-fund/documents/{task_id}/nl2sql-fields`,返回最新净值、基金最新总份额、申请前持有份额、当前最新可用份额,并区分 `success` / `query_failed` / `not_queried`(当前单据类型不查询)/ `pending`(尚未核对)四种状态,同时返回每条查询的规则码、状态、返回行数和时间。字段值取自既有 `offsite_rule_result.database_value`,不返回生成过的 SQL;权限码 `offsite:read`,只读事务,审计类别 `offsite.nl2sql_fields_viewed`。集成测试 5 项通过,真实数据接口核对通过。
- [x] 35. 新增规则判定结果查询与重新判定接口。
备注:新增 `GET /api/v1/offsite-fund/documents/{task_id}/rule-results`(申购三条规则、赎回两条规则,返回结论、单据侧值、查询侧值、计算过程和正常/异常/无法判断汇总)与 `POST /api/v1/offsite-fund/documents/{task_id}/rule-results/recalculations`(重新判定)。重新判定只重跑"计算 + 核对"两个阶段,输入是已落库的识别字段和已有 NL2SQL 查询结果,不重新识别、不重新调用 NL2SQL,并返回每条查询的重放结果和被阻断原因。安全边界:仅在 `planned` / `query_failed` 之间流转单据状态,不覆盖 `recognition_exception`、`recognition_review`、`operator_confirmed`,也不改动人工确认结论。权限码读取用 `offsite:read`、重新判定用 `offsite:write`/`offsite:confirm`/`offsite:nl2sql`,审计类别 `offsite.rule_results_viewed` 和 `offsite.rule_results_recalculated`。集成测试 6 项通过(含赎回 30% 判为异常、无查询记录判为无法判断),真实数据核对通过。
- [x] 36. 下线外部写入识别邮件的 HTTP 接口并同步调整前端与测试。
备注:删除 `POST /api/v1/offsite-fund/recognized-mails` 路由与仅它使用的 `OffsiteMailRecognizeRequest` 请求模型;`OffsiteFundService.receive_recognized_mail` 保留,真实收信链路(`OffsiteMailWorker`)仍直接调用该 Service,不受影响。前端移除"识别邮件测试工具"面板及相关状态与动作(否则按钮会打到已下线接口),清算统计面板改为整行展示。集成测试改为通过 `TestClient.portal` 在同一事件循环内直接调用 Service 入库,避免跨事件循环复用连接。后端 15 条场外路由全部实测可用,已删接口返回 404,页面加载无控制台错误。
- [x] 37. 下线清算统计的兼容老路径。
备注:删除 `POST /api/settlement-statistics/recalculate` 兼容路由,保留 `POST /api/v1/offsite-fund/settlement-statistics/recalculate`。影响面核对:前端与 3 处集成测试均使用 `/api/v1` 路径,只有 SKILL 文档引用老路径,已同步修正文档。实测老路径 404、新路径正常返回、同一 `operation_router` 下的核对触发接口不受影响;场外路由现为 14 条,`ruff` 与 24 项场外集成测试通过。
- [x] 38. 保留识别重试入口(评估后决定不下线)。
备注:曾按要求删除 `POST /api/v1/offsite-fund/documents/{task_id}/recognition-retries`,评估后确认它是 `recognition_exception` / `recognition_review` 状态单据的唯一人工恢复入口(确认接口对这两个状态返回 422,同一邮件重投会被 `imap_uid` + `message_id` 幂等挡住),删除后此类单据在页面上无恢复手段,因此恢复该路由。服务层、识别尝试表、前端按钮和集成测试均未改动;恢复后 `ruff`、24 项场外集成测试、真实接口调用全部通过。
- [x] 39. 新增 OCR / NL2SQL 字段人工修正接口,并完善前端识别核对工作台。
备注:新增 `PUT /api/v1/offsite-fund/mails/{mail_id}/recognition-fields` 与 `PUT /api/v1/offsite-fund/documents/{task_id}/nl2sql-fields`,人工修正值写入独立新表 `offsite_field_correction`(迁移 `20260911_field_correction`,只新增表、不改动任何已有表名与字段),保留修正前值、修正后值、变更字段和操作人,不覆盖 Agent 原始识别值与 NL2SQL 查询记录;越权按 `offsite:write` 拦截,审计类别 `offsite.mail_recognition_corrected` 与 `offsite.nl2sql_fields_corrected`。读取接口按需扩展:识别字段新增 `effective_fields` / `corrections` / `has_correction` / `default_state`(识别全部无异常默认 `saved`,存在缺失或低置信字段默认 `editing`),NL2SQL 字段新增 `effective_fields` / `corrections` / `default_state` 固定为 `editing`,`field_status` 增加 `corrected`。重新判定改为使用修正后的有效值:OCR 修正值覆盖识别原值,NL2SQL 修正值优先于查询原值,修正补齐字段后规则不再被阻断。前端新增"OCR 识别字段与 NL2SQL 对比"双栏面板(缺失字段同样给出可填写输入位)、修改/保存/取消状态切换、规则判定结果面板与"重新判定"按钮,并在附件卡片与单据卡片增加"查看原件"入口。验证:新增集成测试 7 项通过;`ruff` 检查本次改动文件通过;`mypy` 本次改动无新增错误;Alembic 单 head、`tools/audit_schema.py`(68 张业务表)通过;浏览器端到端实测通过(补齐识别缺失字段 → 保存 → 保存 NL2SQL 修正 → 重新判定后规则由"无法判断 2"变为"正常 1 / 异常 1")。