# CR-032　「歸戶 360」頁面之設計與實作計畫

| 項目 | 內容 |
|---|---|
| 文件編號 | CR-032 |
| 版本 | **1.1**（**已執行完畢**） |
| 提出日 | 2026-10-01 |
| 最後修訂 | 2026-10-02（**1.1**：實作完成並通過**真實 Chrome 端到端驗證**，見 §7） |
| 提出者 | WorkBuddy AI（開發團隊） |
| 緣起 | BEAM PMO 2026-10-01：「**好期待『歸戶 360』頁做出來，先來這個**（鐵律：擬定計劃、規格先行、任務追蹤清單）」——即 `CR-031 §4.5` 與 `SAD-004 §2.2.1.1` 所**誠實揭露**之「`BffAccountOverview` 尚無前端消費者」之**閉環** |
| 依據 | `SAD-004 §2.2.1`／**`§2.2.1.1`**（四區塊聚合）、`SDD-007 §5.4.3`（持卡人／歸戶查詢）、`SCS-004 §7.5`（卡片狀態投影）、`GOV-003` 條件 9（無規格不寫程式） |
| 範圍 | 新增 WebUI 頁面 **`/accounts`「歸戶 360」**（唯讀跨域總覽） |
| **不在範圍** | **無契約變更、無後端變更**——後端聚合已於 `#A-144` 完成（`CR-031 §4.5`） |

---

## 1. 為什麼要做（**閉環**理由）

`#A-144` 完成後，`BffAccountOverview` 已能回**四區塊**，惟**無前端消費者**——
「**有能力、沒人用**」（`SAD-004 §2.2.1.1` 已揭露）。

本 CR 補上消費者，使三批工作形成**完整可驗證之鏈路**：

```
#A-143  card.card.status-changed.v1  ──事件──▶  CC_PARTY.card_status_caches   （投影）
#A-144  BffAccountOverview           ──聚合──▶  四區塊（party／cards／limits／rewards）
#A-145  /accounts 「歸戶 360」        ──呈現──▶  客服一眼看見客戶全貌        （本 CR）
```

> ⚠️ **若不做本頁**，`#A-144` 即為「**有能力、沒人用**」之死碼——這正是 `CR-027 §7`
> 「不為『打通』而建無語意來源之物」所警示者。

---

## 2. 設計裁定

### 2.1 新頁 `/accounts`，**不併入** `§5.4.3`（`/parties`）

| 理由 | 說明 |
|---|---|
| **職責不同** | `§5.4.3` 為 **party 域作業頁**（貼標、管制碼——**有寫入**）；本頁為**跨域唯讀總覽** |
| **存取路徑不同** | `§5.4.3` 直呼 `party-service`（`/party-api`）；本頁走 **BFF 聚合**（`/bff-api`）。**一頁兩條存取路徑**會使快取、錯誤、權限語意混雜 |
| **重疊之處理（刻意）** | 兩頁都會顯示「歸戶主檔 ＋ 持卡人清單」——**總覽頁必須自足**，否則使用者仍要跳頁。惟本頁**唯讀**，並於畫面明示「**異動請至「持卡人／歸戶查詢」**」 |

> ⚠️ **誠實揭露**：本頁**超出** `SDD-007 §5.1` 之 **59 頁**清單
> （先例：`/limit-adjustments`，同為「本案額外建置，不計入 59 頁」）——故本頁為**第 2 頁**超出者。

### 2.2 路由與角色

| 項 | 值 |
|---|---|
| 路由 | **`/accounts`** |
| 側欄標籤／頁首標題 | **歸戶 360** |
| 服務標示 | `bff-web:8101` |
| **所需角色** | **`CSR`**（客服）——四區塊之下游讀取權限分別為 party（`CSR`）／limits（已認證）／rewards（`CSR`）→ 取**最嚴者** |

### 2.3 版面（單頁、由上而下）

```
① 查詢列：歸戶識別碼（UUID）＋ [查詢] ＋ [清除]
② 歸戶主檔（含持卡人清單）      ← 區塊 party
③ 卡片狀態清單                  ← 區塊 cards
④ 額度                          ← 區塊 limits
⑤ 紅利點數                      ← 區塊 rewards
```

### 2.4 ★★ 降級呈現（**本頁最重要之規則**）

四區塊各自 `OK`／`UNAVAILABLE`。**`UNAVAILABLE` 必須明顯呈現**（**顯示下游錯誤碼與訊息**）。

> ⚠️ **嚴禁**以 `0`、`-`、空白表格代替 `UNAVAILABLE`。
> **「取不到」與「真的是 0／空」是兩種不同事實**——混為一談會讓客服做出錯誤判斷
> （例：額度取不到卻顯示 `0` → 客服誤以為客戶無額度）。
> 此為 `SCS-020 §9.11`（營運總覽）同一原則之延伸。

### 2.5 空狀態 vs 不可用（**必須區分**）

| 情境 | 呈現 |
|---|---|
| `cards` 為 `OK` 且 `data: []` | 「**此歸戶尚無卡片**」——**合法狀態**（`SCS-004 §7.5`） |
| `cards` 為 `UNAVAILABLE` | 「**不可用**：`CC-PTY-4040` 查無歸戶」——**失敗** |
| `limits` 為 `UNAVAILABLE`（`CC-LIM-1001`） | 「**不可用**：該歸戶尚無額度主檔」——**非 0** |

### 2.6 PII／PCI

- 卡號僅顯示**遮罩值**（`cardNoMasked`，如 `411111******9682`）——**不含 PAN**。
- 歸戶／持卡人之 PII（身分證／手機／電郵）一律顯示**後端回傳之遮罩值**，前端**不解密、不自行遮蔽**（`SAD-005 §5`）。

### 2.7 快取

BFF **不快取**（即時聚合）→ 每次查詢皆為真實下游呼叫。
前端以 `react-query` 依 `accountId` 快取；**重查需明確觸發**（本頁僅「查詢」鈕觸發，**不做自動輪詢**）。

---

## 3. 資料來源（**無契約變更、無後端變更**）

| 項 | 值 |
|---|---|
| 端點 | `GET /bff-api/v1/bff/accounts/{accountId}/overview` |
| 契約 | `bff-web.yaml` 之 `BffAccountOverview`（**四區塊**；`#A-144` 已納入 `gen_openapi.py`） |
| 前端代理 | Vite `/bff-api` → `localhost:8101`（**既有設定，無需改動**） |

---

## 4. 執行步驟（依鐵律）

```
① 規格先行   SDD-007 §5.4.31（逐鈕／逐區塊定義）
② 前端 API   frontend/src/api/bff.ts（型別 ＋ fetchAccountOverview）
③ 頁面       frontend/src/pages/AccountOverviewConsole.tsx
④ 註冊       frontend/src/App.tsx（PAGES：側欄／路由／頁首**同源**）
⑤ 關卡       typecheck ＋ build
⑥ 真機驗證   §5
⑦ 追蹤清單   HANDOVER ＋ GOV-006
```

---

## 5. 驗證計畫（**真機**）

| # | 樣本 | 預期 |
|---|---|---|
| 1 | 歸戶 `…0001`（有卡、有額度） | 歸戶 **OK**／**卡片 2 張**／額度 **OK**／點數 **UNAVAILABLE（`CC-RWD-4040`）**——★ **UNAVAILABLE 必須看得出來，且不得顯示為 0** |
| 2 | 歸戶 `…0010`（有持卡人、有點數） | 歸戶 **OK**／持卡人 **2 位**／卡片 **OK 但空 →「尚無卡片」**／點數 **OK** |
| 3 | 卡片清單逐欄比對 | 與 `CC_PARTY.card_status_caches` **完全一致** |
| 4 | 非 UUID 之輸入 | **400 `CC-PLT-4003`** 如實呈現 |
| 5 | ★ **負樣本**：BFF 之 party 下游指向**死埠** | 歸戶與卡片兩區塊顯示**不可用＋錯誤碼**；**額度／點數仍正常顯示**（降級為**逐區塊**） |
| 6 | 未登入 | **401** → 前端導向登入（`SCS-022`） |

---

## 6. 明確不做（**避免同一功能兩套 UI**）

| 不做 | 應至 |
|---|---|
| 卡片狀態**異動**（開卡／鎖卡／補發） | `/card/lifecycle`（`§5.4.16`） |
| 額度調整／佔用 | `/limit-adjustments`（`§5.4.2`）／`/card/limit`（`§5.4.18`） |
| 點數入點／兌換 | `/rewards`（`§5.4.7`） |
| 帳單／入帳明細 | `/statements`（`§5.4.5`）／`/postings`（`§5.4.4`） |
| 貼標／管制碼 | `/parties`（`§5.4.3`） |

→ 本頁為**唯讀總覽**；各區塊附「**異動請至〈頁名〉**」之文字指引。

---

## 7. 執行結果（2026-10-02 完成）

| 步驟 | 產出 |
|---|---|
| ① 規格先行 | `SDD-007` **1.27** 新增 **§5.4.31**（逐鈕／逐區塊定義） |
| ② 前端 API | `frontend/src/api/bff.ts`——`BffSection`／`BffProjectedCard`／`BffAccountOverview` ＋ `fetchAccountOverview`（**沿用既有 `AccountView`／`LimitView`／`PointsAccountView`**） |
| ③ 頁面 | `frontend/src/pages/AccountOverviewConsole.tsx` |
| ④ 註冊 | `frontend/src/App.tsx`（側欄 **29 → 30 頁**） |
| ⑤ 關卡 | `typecheck` **0 錯誤**；`build` **成功**（131 模組） |
| ⑥ 真機驗證 | 見下 |

**真機驗證（真實 Chrome 154 ＋ CDP ＋ Keycloak 登入）**

| # | 樣本 | 實測 |
|---|---|---|
| 1 | 未登入 | **導向 Keycloak 登入頁**（fail-closed 生效） |
| 2 | `demo.csr` 登入後 | 側欄 **30 頁**、含「歸戶 360」；頁首 `歸戶 360 / bff-web:8101` |
| 3 | 歸戶 `…0001` | 歸戶 **可用**／卡片 **可用（2 列）**／額度 **可用**／點數 **不可用 ＋ `CC-RWD-4040`**（★ 錯誤盒呈現，**未以 0 代替**） |
| 4 | 歸戶 `…0010` | 歸戶 **可用（2 位持卡人）**／卡片 **可用但空 →「此歸戶尚無卡片。（此為合法狀態，非查詢失敗。）」**／額度 **不可用 ＋ `CC-LIM-1001`**／點數 **可用（CUR 100,003／AV1 0／AV2 0——真實 0）** |
| 5 | 非 UUID | **`CC-PLT-4003`** 如實呈現 |

> ★ **第 3／4 項同時證明兩件事**：**(a) 逐區塊降級**（一區塊不可用不拖垮其餘）；
> **(b) 「不可用」與「真實 0／空」在同一畫面上明確區分**（`…0010` 之點數 AV1／AV2 為**真實 0** 並正常顯示為 `0`，
> 而同畫面之額度為**不可用**並顯示錯誤碼——**兩者外觀不同**）。
>
> **證據**：`docs/08-evidence/A-145_歸戶360頁_四區塊正常與獨立降級_20261001.png`／
> `A-145_歸戶360頁_空狀態與不可用區分_20261001.png`。

**三批閉環**：`#A-143`（事件投影）→ `#A-144`（BFF 四區塊聚合）→ **`#A-145`（本頁呈現）**。

---

## 8. 修訂記錄

| 版本 | 日期 | 說明 |
|---|---|---|
| 1.1 | 2026-10-02 | **實作完成並通過真實 Chrome 端到端驗證**（新增 §7）。`SDD-007` 升 **1.27**；側欄 **29 → 30 頁**；證據 **45 → 47 張**；`GOV-006` WBS **1.3.2 完成** |
| 1.0 | 2026-10-01 | 初版：依 BEAM PMO 指示擬定計畫。裁定**新頁 `/accounts`「歸戶 360」**（不併入 `§5.4.3`——職責與存取路徑皆不同）；★ **降級呈現為本頁最重要規則**（`UNAVAILABLE` 不得以 `0`／空白代替——「取不到」≠「真的是 0」）；區分**空狀態**與**不可用**；**無契約／後端變更**；★ **誠實揭露**本頁**超出 `§5.1` 之 59 頁清單**（第 2 頁超出者，先例 `/limit-adjustments`） |
