多張保單 OCR — 整合指南
本文涵蓋流程、跨 endpoint 概念、業務情境,協助前端與 PM 快速理解全貌。
欄位 / Request / Response 細節請查 apidocs(單一真相來源)。
欄位 / Request / Response 細節請查 apidocs(單一真相來源)。
本案做了什麼
新增「保險公會保險存摺投保紀錄表」一次辨識多張保單功能;既有單張 OCR 流程同步擴充加密 PDF 支援與要保人比對(對不到既有客戶名單時可保存 OCR 原字串)。
向後相容:所有新欄位 absent 時走既有單張 OCR 行為,老版 App 不受影響。
向後相容:所有新欄位 absent 時走既有單張 OCR 行為,老版 App 不受影響。
Endpoint 速查
點 Endpoint 名稱跳至 apidocs 看完整欄位規格。
| Endpoint | 狀態 | 本案變更摘要 | 用途 |
|---|---|---|---|
| IE_S_102 | 擴充 | +isMultiPolicy / +filePassword;response +isEncrypted / +quotaRemaining | 上傳檔案(單張 / 多張共用入口) |
| IE_S_105 | 擴充 | response +isMultiPolicy / +policyCount / +quotaRemaining + 多張新階段(pdf_parse / meta_extract) | 輪詢狀態 + 重入檢查 |
| IE_S_103 | 擴充 | response +holderMatch / +isMultiPolicy / +policyCount / +quotaRemaining;多張時 policyData=null | 單張結果取回 |
| IE_S_113 | 新增 | 多張完整結果(per-policy holderMatch / insuredMatch / productMatch / premiumEstimate) | 多張結果取回 |
| IE_S_107 / IE_S_108 | 擴充 | + optional recognizedPolicyId(多張時必填) | 商品候選清單 / 切換 |
| IE_S_116 | 新增 | atomic 批次儲存 + 逐欄位錯誤回應 | 多張批次儲存 |
| IE_S_104 / IE_S_106 | 不變 | — | 單張確認 / 放棄(多張不需 104,116 自動 invalidate) |
| IE_S_54 / IE_S_59 | 擴充 | +applicantRawString(IE_S_54);+applicantRawString / +holderMatch(IE_S_59) | 既有單筆保單編輯 / 取得 |
多張 OCR 完整流程
App 與後端的時序圖,標出每個 endpoint 在生命週期中的位置。
FIG 1 — 多張 OCR 上傳 → 輪詢 → 取結果 → 儲存
看完上圖你應該知道
(1) 多張流程共 4 個必經 endpoint:102 → 105 → 113 → 116;(2) IE_S_105 的
isMultiPolicy 是 dispatch 關鍵;(3) IE_S_107/108 是選用(使用者切換商品時才打);(4) IE_S_116 失敗時用 failures[].fieldErrors[].field 定位卡片。
單張 / 多張 Dispatch 決策
App 在每個關鍵點都要用
isMultiPolicy 決定走單張路徑還是多張路徑。FIG 2 — Client dispatch 決策樹
為什麼需要 dispatch
單張結果結構與多張結果結構不同(多張是陣列、有 unknownFieldsHint / insuredCandidate)。
isMultiPolicy 是 server 在 session 建立時記下、App 重入時取出的「我這個 session 是哪一種」。App 不應自己記錄這個 flag — server 是單一可信來源(支援跨裝置 + kill-restore)。
加密 PDF 錯誤路徑
PDF 密碼問題在 IE_S_102 上傳時同步判定,不會進入 PROCESSING、不會在 IE_S_105 輪詢時出現。
FIG 3 — 加密 PDF 同步錯誤(967 / 970)
967 OCR_PDF_ENCRYPTED — PDF 加密但 App 沒帶 filePassword(fallback;應由 pdf.js 預先攔截)
970 OCR_WRONG_PASSWORD — 密碼錯誤(App 端 inline error,可重新輸入後重送)
967 / 970 不消耗額度。額度只在 OCR 辨識成功階段扣 1 次。
Kill-Restore / 跨裝置重入
使用者切換 tab / kill app / 換裝置後,App 必須能正確接回未完成的 OCR session。
FIG 4 — 重入時的 dispatch(IE_S_105 是唯一入口)
Client 不本地持久化 OCR 狀態
為了支援跨 app lifecycle(kill-restore)與跨裝置同帳號同步,server 是 OCR session 狀態的 single source of truth。每次回到 OCR 頁面都先打 IE_S_105,從 response 取狀態,不從本地 cache 取。
核心概念 1:recognizedPolicyId
多張流程中每張保單的 correlation id,串連 IE_S_113(取結果) ↔ IE_S_107/108(商品切換) ↔ IE_S_116(批次儲存) 三個 endpoint,定位「哪一張保單」。後端不寫 DB、不理解格式,純做 echo back。
FIG 5 — recognizedPolicyId 生命週期
核心概念 2:額度扣抵時機
單張 / 多張共用每日 20 次。額度只在 OCR 辨識成功時扣 1 次,不在儲存階段扣。
| Endpoint | 是否扣額度 | 說明 |
|---|---|---|
| IE_S_102 上傳 | ❌ | 上傳成功不扣(含加密 PDF 967/970 同步錯誤) |
| IE_S_105 輪詢 | ❌ | 輪詢狀態,不影響額度 |
| OCR 辨識成功 | ✅ 扣 1 次 | 內部由 OCR pipeline 完成階段觸發;前端觀察點:105/103/113 首次回 ocrStatus="SUCCESS" 時,quotaRemaining 已下降 |
| IE_S_103 / IE_S_113 取結果 | ❌ | 不扣(即使首次取結果觀察到 quotaRemaining 變化,是 OCR pipeline 上一步扣的) |
| IE_S_104 / IE_S_106 / IE_S_116 | ❌ | 確認 / 放棄 / 批次儲存皆不扣 |
| 辨識失敗 / 放棄 / 解密失敗 | ❌ | OCR 沒成功 → 不扣 |
前端顯示策略
所有 response 都有
quotaRemaining,App 直接顯示後端回值即可,不要自己算。額度扣抵時機完全由後端決定。
核心概念 3:暫存(OCR session)生命週期
一個 OCR session 從 IE_S_102 上傳 開始建立,保留 24 小時。同一使用者同時只能有一個未完成 session,重複上傳會回 953。
FIG 6 — Session state 轉換
953 已有未完成 OCR 任務 — 同一 memberId 想再打 IE_S_102 時,需先用 IE_S_104 / IE_S_106 / IE_S_116 清掉前一個 session
952 暫存已過期 — IE_S_103 / 113 / 107 / 108 / 116 等想取已逾 24h 的 session 時回
業務情境決策表
常見情境對應的 endpoint、UX 處理、邊界注意事項。
情境 1
使用者上傳加密 PDF
| 步驟 | App 動作 | API |
|---|---|---|
| 1 | App 用 pdf.js 偵測加密 → 跳「解密同意書」頁讓使用者輸入密碼 | — |
| 2 | 本地驗證密碼正確 → 將「原始加密 binary + filePassword」一起上傳 | IE_S_102 |
| 3a | 同步收到 970(後端解鎖失敗) → inline error,使用者可重輸密碼 | — |
| 3b | 同步收到 967(沒帶密碼) → 補帶密碼後重打 | — |
| 3c | 成功收到 sessionId + isEncrypted=true → 後續正常輪詢 | — |
情境 2
OCR 辨識成功但要保人對不到既有客戶
情境 3
商品自動比對失敗 / 使用者想換商品
情境 4
使用者切換 tab / kill app / 換裝置後回到 OCR 頁
情境 5
批次儲存有 1 張保單欄位驗證失敗
| 步驟 | App 動作 | API |
|---|---|---|
| 1 | 使用者按「全部完成」→ 帶完整 policies[] | IE_S_116 |
| 2 | 收到 HTTP 200 + result="955" + failures[] | — |
| 3 | 遍歷 failures[],按 recognizedPolicyId 找到對應卡片 | — |
| 4 | 依 fieldErrors[].field 路徑(如 policyList.0.amount)自動滾到對應 UI 欄位 + 紅字提示 | — |
| 5 | 修正後重打 IE_S_116(整批 atomic,不需挑出失敗的單獨送) | IE_S_116 |
HTTP 仍為 200,需以
result 判斷。本 endpoint 不扣額度,失敗重打不影響額度。
情境 6
OCR 辨識失敗(非公會表格 / 資訊不全)
| 狀況 | errorCode | 建議文案 |
|---|---|---|
| PDF 內容非公會表格 | 969 NOT_POLICY_FORMAT | 「無法辨識為公會投保紀錄表」 |
| 辨識內容資訊不全 | 971 INFO_INCOMPLETE | 「保單資訊辨識失敗,請改用其他方式上傳」 |
| 其他 OCR 失敗 | 950 / 958–966 | 顯示後端回的 message(已客製化文案) |
辨識失敗皆不扣額度。errorCode 出現在 IE_S_105 / IE_S_103 / IE_S_113 的 response 內,非 HTTP 錯誤。
前端整合 Checklist
上傳階段
- 多張入口 request 帶
isMultiPolicy=true;單張入口維持既有行為(不帶或 false) - 加密 PDF:pdf.js 本地驗證密碼正確後,將「原始加密 binary + filePassword」一併送 IE_S_102(不送明文 PDF)
- 處理 953 重複任務 → 引導先確認 / 放棄
- 處理同步 967 / 970 密碼錯誤 → inline error,不算辨識失敗(不會扣額度)
輪詢階段
- 進入頁面先 IE_S_105 檢查未完成暫存(含跨裝置 / kill-restore)
- 上傳後 IE_S_105 每 2–3 秒輪詢;UI 顯示
stageLabel+progress - 多張新增階段
pdf_parse/meta_extract要有對應中文文案 - FAILED 時讀
errorCode,特別針對 969 / 971 客製文案
取結果階段
- SUCCESS 時依
isMultiPolicydispatch:true → IE_S_113;false → IE_S_103 - IE_S_113 收到後為每張保單覆寫自己生成的
recognizedPolicyId(建議用 UUID) - 處理
insuredCandidate(被保人姓名不一致對話框) - 處理
unknownFieldsHint(未知欄位提示對話框) holderMatch.matchResult="STRING_ONLY"→ 顯示原字串 + 「資料不全」橘字productMatch.matchType="MANUAL"→ 呈現 recommendations 讓使用者選
儲存階段
- IE_S_116 request 每筆帶
recognizedPolicyId applicantId/applicantRawString二擇一(每筆恰一個非 null)- 處理
result="955":依failures[].fieldErrors[].field自動定位 UI 欄位 - 不需呼叫 IE_S_104(IE_S_116 成功後自動 invalidate 暫存)
quotaRemaining直接顯示後端回值,不自己算
PM Checklist
業務規則確認
- 每日 OCR 額度:單張 / 多張共用 20 次,多張 PDF 內 N 張保單只算 1 次
- 額度扣抵時機:OCR 辨識成功階段扣 1 次;失敗 / 放棄 / 解密失敗皆不扣
- 辨識結果暫存 24 小時
- 同一使用者同時只能有一個未完成暫存
UX 文案確認
- 967 / 970(密碼問題):inline error 文案
- 969(非公會表格):「無法辨識為公會投保紀錄表」
- 971(資訊不全):「保單資訊辨識失敗,請改用其他方式上傳」
- STRING_ONLY 要保人提示:「要保人資料不全,建議擇日補齊」
- insuredCandidate(被保人姓名不一致)對話框文案
- unknownFieldsHint(繳費類別 unknown / 職業等級空)對話框文案
部署 / 整合優先序
| 優先序 | 後端 | App 對應 stage |
|---|---|---|
| 1 | IE_S_102 擴充(additive,無 breaking) | Stage 1:入口 + 既有單張流程修補 |
| 2 | IE_S_105 擴充(依賴 102 persist flag) | Stage 2:多 source dispatch / kill-restore |
| 3 | IE_S_113 / IE_S_116 新 + IE_S_54 擴充 | Stage 3:多保單結果頁 + 批次儲存 |
| 4 | errorCode 967 / 970 / 969 / 971 finalize | Stage 4:errorCode 對映收尾 |
建議:App 端 IE_S_105 dispatch 邏輯 ship 前,後端必須先 ship 同版 IE_S_105(含 isMultiPolicy 欄位)。否則多張 session 重入會被誤判為單張。