收敛真实OCR样票验收入口(07-28-travel-reimbursement-guide)
ci-e2e / e2e (push) Canceled after 0s
ci-minimal / check-minimal (push) Canceled after 0s

这个提交包含在:
2026-07-29 08:28:42 +08:00
父节点 6a1fd4d789
当前提交 fa326f92fa
共修改 7 个文件,包含 464 行新增和 329 行删除
@@ -251,6 +251,103 @@ const persisted = sanitizeOcrResultForPersistence("tencent", normalized);
await prisma.ocrResult.create({ data: { result_json: persisted } });
```
## Scenario: Live OCR Sample Stdin Probe
### 1. Scope / Trigger
- Trigger: an authorized real PDF must prove Tencent credentials, network,
subtype normalization and required-field behavior without creating an OA
invoice or persisting the sample on the execution host.
- This probe is intentionally one-file and PDF-only. It does not retain the
historical manifest, directory scan, image evaluation or legacy VAT-only
command modes.
### 2. Signatures
- Command:
`node scripts/qa/check-ocr-provider-samples.mjs --alias=SAMPLE-PDF-<SAFE_ALIAS>`.
- Input: exactly one PDF on stdin, at most `OCR_PDF_MAX_BYTES` (7 MiB).
- Runtime env: `TENCENT_SECRET_ID`, `TENCENT_SECRET_KEY`, optional
`TENCENT_OCR_REGION` and `OCR_TIMEOUT_MS`; provider is fixed to `tencent`.
- Provider boundary: `runOcrForInvoice({ config, ocrType: "vat",
imageBase64, sourceMimeType: "application/pdf" })`.
- Output: one JSON object containing only `alias`, `provider`, `status`,
`transportMode`, `elapsedMs`, safe document category/subtype/count,
per-field hit booleans, missing field names and a bounded error code.
### 3. Contracts
- The caller selects one authorized PDF explicitly and streams it through
stdin. The probe never accepts a file path, directory, manifest, image or
batch list and never writes sample bytes to disk, OA storage or the database.
- Alias must match `SAMPLE-PDF-[A-Z0-9_-]+`; it replaces the original file name
in every output and evidence record.
- Validate the 7 MiB limit, PDF version header and terminal `%%EOF` before
constructing Base64 or invoking Tencent. One process performs at most one
provider call.
- Reuse `RecognizeGeneralInvoice`, `sanitizeOcrResultForPersistence` and the
production category-specific required-field rules. The probe must not carry
a parallel VAT-only field list.
- Never output recognized identifiers, dates, amounts, buyer/seller/passenger
names, provider messages, raw response or input bytes. Field presence is a
boolean; errors are bounded stable codes.
- A PASS proves the direct provider/normalization path only. It does not prove
OA upload, queue, persistence, duplicate detection, binding or reimbursement
submission.
### 4. Validation & Error Matrix
- Missing/invalid alias -> `sample_alias_invalid`; no provider call.
- Empty stdin -> `sample_empty`; no provider call.
- More than 7 MiB -> `sample_too_large`; stop reading and do not call provider.
- Invalid header or missing terminal EOF -> `sample_pdf_invalid`; no provider
call.
- Missing credentials or provider mismatch -> `ocr_credentials_missing` /
`ocr_provider_invalid`; no business values in output.
- Supported subtype with missing category-required fields -> `status=HOLD`
plus field names only.
- Tencent failure -> `status=HOLD` plus a bounded SDK error code; omit the
provider message and raw error object.
### 5. Good/Base/Bad Cases
- Good: stream one railway electronic ticket using an anonymous alias; receive
`electronic_train_ticket`, boolean field hits and no recognized values or
persisted sample.
- Base: stream one supported accommodation invoice and receive a PASS summary
for its VAT/full-VAT category.
- Bad: point the probe at a directory, auto-run every PDF, write a manifest with
real paths, accept images, print OCR values or retain the sample remotely.
### 6. Tests Required
- Unit tests inject synthetic provider results for VAT, full VAT and railway
categories and assert category-aware required fields.
- Negative tests assert non-PDF, truncated PDF and oversized input fail before
the provider mock is called.
- Privacy tests seed raw provider and recognizable normalized markers, then
assert serialized output contains only booleans and safe metadata.
- CLI test pipes one synthetic PDF over stdin with credentials absent and
asserts one parseable redacted error object with empty stderr.
- Before deployment run `node --check`, the focused tests, full backend tests,
sensitive-content scan, task validation and Trellis governance.
### 7. Wrong vs Correct
#### Wrong
```bash
PLAYWRIGHT_OCR_LIVE_DIR=<sample-dir> \
node scripts/qa/check-ocr-provider-samples.mjs --provider=tencent --json
```
#### Correct
```bash
node scripts/qa/check-ocr-provider-samples.mjs \
--alias=SAMPLE-PDF-TRAIN < <authorized-single-sample.pdf>
```
## Scenario: Payment Batch Receipt Attention Count
### 1. Scope / Trigger
@@ -44,6 +44,14 @@
7. 只记录 provider、状态、尝试/完成证据和脱敏字段命中摘要;不记录票面、完整字段或原始 payload。
8. 正常清理或归档测试业务记录,再允许杨俊提交真实票据。
### 授权样票验证工具边界
- 既有 `check-ocr-provider-samples.mjs` 必须与生产 adapter 使用同一 `RecognizeGeneralInvoice` 入口和按票种必填字段规则,不得继续引用已删除的 VAT-only 导出或把铁路电子客票套入 VAT 五字段门禁。
- 真实 PDF 从本地受控、Git 忽略的样票目录按匿名别名选择,经 SSH 标准输入直接流入生产 server 容器内存;不得把原件、临时副本或 manifest 写入远端磁盘、OA 附件存储、数据库、仓库或报告。
- 每个进程只接收一份 PDF,验证文件签名和 7MiB 上限后最多调用一次 `RecognizeGeneralInvoice`。住宿与铁路样票分两次执行,任一失败立即停止,不自动遍历目录中的其他文件。
- 输出只允许匿名别名、provider、票种、安全传输模式、耗时、字段命中布尔值、缺字段名和稳定错误码;不得输出文件名、原始响应、识别值、人员/企业名称、票号、金额或票面内容。
- live runner 的代码、合成测试和生产部署先通过,再单独取得两次真实 API 调用批准;工具可用不等于真实样票已验证。
## 文档口径
- `docs/员工快速上手.md` 增加一页式差旅报销步骤:火车票和住宿费拆明细,官方 PDF 绑定对应明细,两类费用均附支付截图,金额差异必须说明,提交后进入主管和财务流程。
@@ -44,6 +44,8 @@
- [x] 检查公网 `/ready` 和预期构建标识。
- [x] 在 server 内运行现有 external deps 和运行时 smoke,不输出 Secret 或原始响应。
- [x] 确认实际 provider 为 `tencent`,且只有一个 OCR job executor。
- [x] 修正 live 样票脚本:复用 `RecognizeGeneralInvoice`、按票种必填字段和安全持久化白名单,支持单 PDF 标准输入且仅输出脱敏摘要。
- [ ] 使用合成 PDF/provider 响应覆盖住宿、铁路、非 PDF、超限、缺字段和原始响应不输出,并完成本地门禁、提交和生产部署。
- [ ] 经单独样票批准后分别运行住宿电子普通发票和铁路电子客票 live OCR PDF 验收。
- [ ] 只保存脱敏状态、耗时、provider 和字段命中摘要;确认不存在 mock 调用证据。
- [ ] 按正常 API/业务生命周期清理或归档测试记录,并执行只读残留检查。