# CR-026　營運總覽之業務 KPI 統計端點（補既有缺口）

| 項目 | 內容 |
|---|---|
| 文件編號 | CR-026 |
| 版本 | **1.3** |
| 提出日 | 2026-10-01 |
| 提出者 | WorkBuddy AI（開發團隊） |
| 緣起 | BEAM PMO 指示「**立 CR for 業務 KPI（需各服務統計端點）並執行此任務**」。`#A-111`（`/dashboard`）交付時**明訂範圍**為「系統營運面」，**業務 KPI 屬第二階段**——本 CR 即為該第二階段之規格與實作 |
| 影響範圍 | `tools/gen_openapi.py` → `card-service`／`authorization-service`／`dispute-service`／`bff-web` 四份契約；`SCS-003`／`SCS-001`／`SCS-012`／`SCS-020 §9.11`；四服務之 Repository／Service／Controller／DTO；前端 `api/bff.ts`／`pages/DashboardConsole.tsx` |
| 狀態 | ✅ **實作完成／單測通過／真機驗證通過／前端瀏覽器實測通過（2026-10-01）** |

---

## 1. 缺口之證據

`#A-111` 交付 `/dashboard` 時，**回訪查證**發現：

| 查證項 | 結果 |
|---|---|
| 掃全數 **17 服務**之 `contracts/openapi/*.yaml` | **皆無「統計／摘要」端點**（僅 CRUD 與列表） |
| 因此 | **業務 KPI（發卡量／授權量／待辦件數）無法由既有端點推得** |

→ 故 `#A-111` **明訂範圍界定**（`SCS-020 §9.11.1`）：**本版為系統營運面**；
**業務 KPI 屬第二階段**（需各服務統計端點）——**不假裝業務儀表板已交付**。
**本 CR 即為第二階段之補實作。**

> ⚠️ **不得以「服務有端點」推斷「已能呈現業務指標」**——列表端點無 `total`、
> 且無聚合語意（如「待審件數」須依狀態計數）。故**必須新增專用統計端點**。

## 2. 變更內容

### 2.1 新增端點（4 支／3 服務）

| 服務 | 方法 | 路徑 | 語意 | 回應 |
|---|---|---|---|---|
| `card-service` | `GET` | `/api/v1/cards/stats` | 卡片統計 | 200 `CardStatsView` |
| `card-service` | `GET` | `/api/v1/card-applications/stats` | 進件統計（**含待審件數**） | 200 `CardApplicationStatsView` |
| `authorization-service` | `GET` | `/api/v1/authorizations/stats` | 授權統計（**含待後送數**） | 200 `AuthorizationStatsView` |
| `dispute-service` | `GET` | `/api/v1/disputes/stats` | 爭議統計（**含未結案件數**） | 200 `DisputeStatsView` |

### 2.2 狀態值域（**取自 DDL 實測，不臆造**）

| 服務 | 欄位 | 值域 | 來源 |
|---|---|---|---|
| `card-service` | `cards.status` | `UNACTIVATED`／`ACTIVE`／`SUSPENDED`／`CLOSED` | `V1__baseline.sql` CHECK |
| `card-service` | `cards.block_code` | `INACTIVE`／`ACTIVE`／`LOCKED`／`LOST`／`REISSUED`／`EXPIRED`／`CLOSED` | 同上 |
| `card-service` | `card_applications.status` | `RECEIVED`／`REVIEWING`／`SUPPLEMENT`／`APPROVED`／`REJECTED` | 同上 |
| `authorization-service` | `authorizations.decision` | `APPROVED`／`DECLINED`／`REFERRAL`／`STANDIN` | 同上 |
| `dispute-service` | `dispute_cases.status` | `OPEN`／`EVIDENCE_PENDING`／`REPRESENTED`／`WON`／`LOST`／`CLOSED` | 同上 |

### 2.3 設計決策

| 決策 | 理由 |
|---|---|
| **「待辦」之定義須明載** | `pendingReview`＝`REVIEWING`＋`SUPPLEMENT`（**非** `RECEIVED`——已受理未送審**不屬**待審）；`pendingUpload`＝`standin_flag=1 AND standin_upload_at IS NULL`（**`V4__standin_upload.sql` 之權威註解**）；`openCases`＝`status NOT IN ('WON','LOST','CLOSED')`（**非終態**） |
| **計數以 `GROUP BY` 回「各狀態 → 筆數」** | 前端**不需**自行推導（避免兩套真相）；且**新增狀態值時前端不會靜默漏算**（後端回什麼就顯示什麼） |
| **`total` 與分組並存** | `total` 供快速顯示、分組供明細；**兩者皆由後端計算**（前端不自行相加——若後端有未列於分組之狀態，相加會少算） |
| **金額一律最小單位（分）** | 全案慣例（`Money.amount`）；惟本 CR 之統計**不含金額彙總**（避免與帳務口徑混淆，**另立 CR**） |
| **不快取** | 同 `§9.11.3`——統計須即時；快取會顯示過期數字（比不顯示更危險） |
| **`bff` 之 `business` 區塊獨立降級** | 任一服務統計取不到 → 該子區塊為 `null`，**不拖垮整體、不以 0 充數**（同 `§9.11.3`） |

## 3. 範圍（誠實揭露）

**第一階段（本 CR）**：4 個端點，涵蓋「**發卡／進件／授權／爭議**」四類**件數**統計。

**未納入（後續）**：

| 項 | 原因 |
|---|---|
| **金額彙總**（如授權總金額、帳單總金額） | 涉及帳務口徑（是否含沖正／退款），**須與財務確認** → **另立 CR** |
| **趨勢／時序**（如近 7 日發卡數） | 需讀模型或時序表；且**瀏覽器畫面不適合呈現長期趨勢**（`§9.8.4`）→ 仍由 Prometheus／Grafana 負責 |
| `statement`／`payment`／`rewards` 等服務之統計 | 需先確認 KPI 定義與業務口徑 → **後續階段** |
| 前端**真實瀏覽器實測** | ✅ **已完成（2026-10-01）**——headless Chrome 實測 `/dashboard`：業務 KPI 盒正常顯示（卡片 17／進件 7／授權 38／爭議 2）；獨立降級顯示「不可用——此子區塊彙整失敗」（**非 0**）。截圖見 `docs/08-evidence/CR-026_前端業務KPI盒_*.png` |

## 4. 驗證

| 項 | 結果 |
|---|---|
| 契約重生（`gen_openapi.py`） | ✅ 0 錯誤；端點數 card-service 9→11（新增 `cards/stats`＋`card-applications/stats`）、authorization-service 9→10（新增 `authorizations/stats`）、dispute-service 3→4（新增 `disputes/stats`）、bff-web 5（新增 `business` 區塊＋5 個 `BffDashboardBusiness*` schema） |
| `card-service` 單測 | ✅ `CardStats`／`CardApplicationStats` 經 `Fakes` 真實計算驗證；`:services:card-service:test` BUILD SUCCESSFUL |
| `authorization-service` 單測 | ✅ 3 個測試 `FakeRepository` 補 `stats()`（沿用 `UnsupportedOperationException` 慣例）；`:services:authorization-service:test` BUILD SUCCESSFUL |
| `dispute-service` 單測 | ✅ 新增 `statsReflectsState` **真測**（in-memory 計算，非假測）；`:services:dispute-service:test` BUILD SUCCESSFUL（20 例 0 失敗） |
| `bff-web` 單測 | ✅ 新增 `businessBlockFlowsThrough`／`businessFailureYieldsNullButOthersSurvive`；`:platform:bff-web:test` BUILD SUCCESSFUL |
| 掃描器 | ✅ `scan_scaffold`／`scan_mockdata`／`scan_faketest` 於 4 模組皆 **0 命中**；`validate_mermaid` **0 錯誤**（117 區塊）；`validate_sql` 不適用（本 CR 未新增 DDL migration，僅運行期 SELECT） |
| 前端 type-check | ✅ `npm run typecheck` **0 錯誤** |
| 真機驗證（對真 Oracle） | ✅ **已完成**：4 stats 端點 `total` 與 Oracle 直接 `COUNT(*)` 零差異；Oracle `V$SQL` 證明即時 SQL 查詢（非快取）；BFF `business` 區塊正確聚合；獨立降級驗證通過（`disputes=null` 非 `0`）。詳見 `CR-026_真機驗證紀錄.md`。前端**真實瀏覽器實測**依 BEAM PMO 指示延後 |

> ⚠️ **驗證誠實揭露**：本 CR 之「**聚合邏輯**」已由單元測試真實執行（`statsReflectsState` 驗證 total／byStatus／openCases 語義；`DashboardAggregationServiceTest` 驗證四區塊獨立降級）；**JDBC `GROUP BY` 之 SQL 對真 Oracle 執行**與 **bff 之 HTTP 呼叫路徑**已於 `CR-026_真機驗證紀錄.md` 完成驗證。前端真實瀏覽器實測仍依 BEAM PMO 指示延後。

---

## 5. 修訂記錄

| 版本 | 日期 | 說明 |
|---|---|---|
| 1.0 | 2026-10-01 | 初版：缺口證據（17 服務皆無統計端點）；新增 4 端點／3 服務；狀態值域取自 DDL 實測；設計決策（待辦定義明載／`total` 與分組並存／不快取／獨立降級）；範圍揭露（金額彙總與趨勢另立） |
| 1.1 | 2026-10-01 | **執行完成（實作＋單測＋契約＋前端）**：①4 端點全部落地；②契約重生 0 錯誤；③四模組 `:test` 全過；④掃描器 0 命中、Mermaid 0 錯誤、前端 type-check 0 錯誤；⑤真機驗證（對真 Oracle）**待 infra 就緒後執行**，前端瀏覽器實測依 BEAM PMO 指示延後 |
| 1.2 | 2026-10-01 | **真機驗證完成**：起 docker infra（Oracle 23ai Free）＋ 4 服務連真庫；curl 4 stats 端點 total 與 Oracle `COUNT(*)` 零差異；Oracle `V$SQL` 證明即時 SQL（非快取）；BFF `business` 區塊正確聚合；獨立降級驗證通過（`disputes=null` 非 `0`）。歸檔：`CR-026_真機驗證紀錄.md`。前端瀏覽器實測仍延後 |
| 1.3 | 2026-10-01 | **前端瀏覽器實測完成**：headless Chrome 載入 `/dashboard`，業務 KPI 盒如實顯示四子區塊（卡片 17／進件 7／授權 38／爭議 2）；停 `dispute-service` 後爭議子區塊顯示「不可用——此子區塊彙整失敗」（**非 0**），獨立降級於前端如實呈現。截圖 2 張歸檔 `docs/08-evidence/CR-026_前端業務KPI盒_{正常,獨立降級}_20261001.png` |
