# SCS-015　遷移波次服務（migration-service）程式設計規格

| 項目 | 內容 |
|---|---|
| 文件編號 | SCS-015 |
| 版本 | **1.2**（`HANDOVER #A-104`：新增 §6.1 遷移作業清單端點 `GET /migration-jobs`——解鎖桶 C「批次作業監控」） |
| 狀態 | **已核定（規格先行）** |
| 對應需求 | `SYS-2.8`（Data Migration）、`SYS-3`（舊資料轉換及查詢） |
| 服務埠號 | 待骨架產生時依 `CSP-006 §4` 分配（自 8081 起編） |
| 套件根 | `com.beam.cardcore.mig` |
| 權威 schema | `CC_MIG`（**尚未建表**） |
| 關聯文件 | `SDD-002 §4`、`BRD-002`、`GOV-005` |

---

## 1. 對應需求

| FR 編號 | 需求名稱 | 來源 |
|---|---|---|
| `SYS-2.8` | Data Migration（資料遷移） | `BRD-002` |
| `SYS-3` | 舊資料轉換及查詢 | `BRD-002` |

### 1.1 上層設計依據（本規格**不重複**這些圖，僅引用）

| 圖種 | 文件 | 數量（實測） | 本服務相關 |
|---|---|---|---|
| **ER-Diagram** | `SDD-001_領域模型與ER-Diagram` | 16 個 `erDiagram` | ⚠️ 本服務 schema 尚未建表（`SDD-001 §3` 僅涵蓋 14 個 bounded context，待 CR） |
| **DFD** | `SDD-002_DFD與流程分解` | 10 個 flowchart | 遷移波次流（`SDD-002 §4`） |
| **Sequence** | `SDD-003_Sequence-Diagrams` | 15 個 `sequenceDiagram` | 遷移波次時序 |
| **狀態機** | `SDD-005_狀態機設計` | 10 個 `stateDiagram` | ⚠️ 遷移波次狀態（`SDD-005` 若有）待確認 |
| **API Contract** | `SDD-006` + `contracts/openapi/migration-service.yaml` | 17 份 | 本服務 2 支端點 |

---

## 2. 服務職責與邊界

**承作**：
- 遷移波次管理（W0–W4 波次編排、停復機）
- 舊資料轉換（Legacy ECS → cardcore 欄位對映、轉碼）
- 回退（rollback）與檢查點（checkpoint）
- 舊資料查詢（`legacy-accounts`）

**不做（屬他服務）**：
- 實際交易處理 → 各領域服務
- 清算／入帳 → `clearing-service`／`posting-service`

---

## 3. 領域模型

- 聚合（依 `services.yaml`）：`MigrationWave`（遷移波次）、`MigrationCheckpoint`（檢查點）
- 值物件：`WaveCode`、`LegacyAccountRef`、`MigrationStatus`
- 狀態（設計意圖）：波次 `{DRAFT, RUNNING, PAUSED, COMPLETED, ROLLED_BACK}`（待 `SDD-005` 正式化）

---

## 4. 程式結構

| 套件／類別 | 職責 | 對應需求 |
|---|---|---|
| `domain/model/MigrationWave` `MigrationCheckpoint` | 聚合與值物件 | `SYS-2.8`/`SYS-3` |
| `domain/port/MigrationRepository` | 持久化埠（待建表） | — |
| `application/MigrationService` | 波次編排／轉換／回退 | `SYS-2.8` |
| `application/LegacyQueryService` | 舊資料查詢 | `SYS-3` |
| `infrastructure/jdbc/*RepositoryImpl` | JDBC 實作（待建表） | — |
| `interfaces/rest/MigrationController` `LegacyAccountController` | REST 端點 | `SYS-2.8`/`SYS-3` |

---

## 5. 資料模型（**設計權威**；`CR-017` 裁定 E：兩層 `WAVE` ＋ `JOB`）

> **背景**：`CC_MIG` 於 V1 基線**完全無表**。BEAM PMO 2026-09-22 裁定 C-8 採
> **兩層（`MIGRATION_WAVES` ＋ `MIGRATION_JOBS`）**——契約之「Job（一次遷移執行）」
> 與 SCS 之「Wave（波次 W0–W4）」為**不同粒度**，兩者並存而非二選一。
> 實體交付：`V2__migration_waves_jobs_and_legacy_map.sql`。

| 表 | 關鍵欄位 | 約束 |
|---|---|---|
| `MIGRATION_WAVES` | `WAVE_CODE` VARCHAR2(16) **UNIQUE**（`W0`–`W4`）；`WAVE_NAME`；`STATUS` DEFAULT `'PENDING'`；`SCOPE` VARCHAR2(400)（影響帳戶範圍說明）；`STARTED_AT`／`FINISHED_AT` | `status IN ('PENDING','RUNNING','PAUSED','COMPLETED','ROLLED_BACK','FAILED')` |
| `MIGRATION_JOBS` | `JOB_NO` VARCHAR2(32) **UNIQUE**；`WAVE_CODE`；`MIGRATION_TYPE`；`SOURCE_REF`；`DRY_RUN` NUMBER(1) DEFAULT 0；`STATUS` DEFAULT `'RUNNING'`；`PROCESSED_COUNT`／`SUCCESS_COUNT`／`ERROR_COUNT`；`ERROR_DETAIL`；`STARTED_AT`／`FINISHED_AT` | `migration_type IN ('ACCOUNT','CARD','BALANCE','POINTS','MERCHANT')`（契約 5 值）；`status IN ('RUNNING','COMPLETED','FAILED')`（契約 3 值） |
| `LEGACY_ACCOUNT_MAPPINGS` | `LEGACY_ID` VARCHAR2(32) **UNIQUE**（如 `LEGACY_ACCT_0001`）；`ACCOUNT_NO`；`MIGRATED` NUMBER(1) DEFAULT 0；`MIGRATED_AT`；`SNAPSHOT` JSON DEFAULT `'{}'` | `UNIQUE(LEGACY_ID)` |

> 含 `CREATED_AT`／`UPDATED_AT` TIMESTAMP(6) WITH TIME ZONE、`VERSION` 樂觀鎖。
>
> **波次狀態機**未建模為 `SDD-005` 正式狀態機（`§10`）——本期狀態值域以 DDL CHECK 為權威，
> **不提供**狀態轉換方法（避免為不存在的流程發明規則）。

### 5.1 ⚠️ 本期 job 之語意（**誠實揭露**）

**實際資料搬遷需舊系統（Legacy ECS）資料源**——該來源**未接入**。
故本期 job 之語意為**「對映註冊表之處理與計數」**：

- 依 `migrationType` 掃描 `LEGACY_ACCOUNT_MAPPINGS` 中 `MIGRATED = 0` 之列；
- `dryRun = true` → **僅計數**（`processed`／`success`），**不變更任何列**；
- `dryRun = false` → 標記 `MIGRATED = 1` 並記 `MIGRATED_AT`；
- ⚠️ **本服務不搬移任何帳務資料**（無來源）——回應中之 `processedCount`／`successCount`
  應理解為**對映處理數**，非「已搬遷之帳務筆數」。**不假造搬遷**。

---

## 6. API 設計（來自 `contracts/openapi/migration-service.yaml`）

| 端點 | 方法 | 說明 | 對應 FR |
|---|---|---|---|
| `/api/v1/migration-jobs` | POST | 遷移波次執行 | `SYS-2.8` |
| `/api/v1/legacy-accounts/{legacyId}` | GET | 舊資料查詢 | `SYS-3` |
| `/api/v1/migration-jobs` | GET | **遷移作業清單**（最近 N 筆；2026-09-30 補建，見 §6.1） | `SYS-2.8` |

> 錯誤碼：`409` 波次衝突、`404` 找不到舊帳號、`400` 參數錯誤。

### 6.1 遷移作業清單端點（2026-09-30 補建，`HANDOVER #A-104`）

> **緣由**：`SDD-007 §5.4.26` 之桶 C「**批次作業監控**」（`/system/batch`）——
> `bff-web` 依 `SCS-020 §9.9` 聚合四服務之批次執行紀錄，**需各服務提供「列出批次」端點**。
> ⚠️ **實測揭露**：`/api/v1/migration-jobs` 原**僅有 `POST`**（受理）——`GET` 實測回 **404**。

**`GET /api/v1/migration-jobs?limit=N`**（回應 `MigrationJobSummary[]`，`200`）

| 參數 | 型別 | 說明 |
|---|---|---|
| `limit` | integer（可選） | 回傳筆數上限。預設 **20**，上限 **100** |

**回應欄位（`MigrationJobSummary`）**：

| 欄位 | 說明 |
|---|---|
| `jobId` | 作業識別碼（UUID） |
| `jobNo` | 作業編號（`MIGJ` ＋ 年月 ＋ 連號，**可稽核**） |
| `waveCode` | 波次（`W0`–`W4`） |
| `status` | `RUNNING`／`COMPLETED`／`FAILED` |
| `dryRun` | 是否試跑（**不變更任何列**） |
| `processedCount`／`successCount`／`errorCount` | ⚠️ **對映處理數**（見 §5.1），**非「已搬遷帳務筆數」** |
| `errorDetail` | 失敗原因（非 `FAILED` 時為 `null`） |
| `startedAt`／`finishedAt` | 開始／完成時刻（未完成者為 `null`） |

> ⚠️ **為何不沿用既有 `MigrationJobView`**：該 DTO 為**受理回應**（`POST`）之形狀，
> **未含 `jobNo`／`startedAt`／`finishedAt`**——清單需時間錨點方能排序
> （`SCS-020 §9.9.4 #3`）。故另立清單專用 DTO，**不動既有已驗證之受理契約**。

**行為要求**：

1. **唯讀**——僅查詢，**不觸發**遷移（觸發為 `POST /migration-jobs`）。
2. **依 `startedAt` 遞減**（最近受理者在前；`startedAt` 為 `null` 者置後）。
3. **無資料回 `200` ＋ 空陣列**——**非** `404`。
4. **不快取**（`SCS-020 §9.9.4 #4`）。
5. ⚠️ **與 `POST /migration-jobs` 同路徑、不同方法**——須明確 `@GetMapping`／`@PostMapping`。

> 錯誤碼：`400` 參數錯誤（`limit` 超出 `1..100`）。**無 `404` 情境**。

**未竟**：

- **不支援分頁游標**（僅取最近 N 筆）；**不含波次彙總**（波次層仍走 `MIGRATION_WAVES` 之既有查詢）。

---

## 7. 關鍵邏輯

- **波次編排**：W0（試遷移）→ W1–W4（分群遷移），每波次可暫停／回退。
- **冪等轉換**：檢查點機制確保中斷後可續跑（參 `common-batch`）。
- **回退**：`ROLLED_BACK` 須能還原至遷移前狀態（雙寫／比對期）。

**`CR-017`（BEAM PMO 2026-09-22 裁定 E）後定案之實作要點**

1. **兩層結構**：作業（`MIGRATION_JOBS`）掛於**波次**（`MIGRATION_WAVES`，`W0`–`W4`）之下
   ——一波次含多個作業。受理時驗證波次存在（否則 `CC-MIG-1001`）。
2. **非同步**：受理僅建立作業（`RUNNING`）即回 **202**；實際處理由排程取件
   （`MigrationJobScheduler`）——狀態由流程推進；冪等以冪等鍵**派生主鍵**。
3. **⚠️ 執行語意為「對映註冊表之處理」（stand-in，`§5.1`）**：
   `dryRun=true` → **僅計數、不變更任何列**；`dryRun=false` → 標記 `MIGRATED=1`。
   **不搬移任何帳務資料**（舊系統資料源未接入）。
4. **失敗可稽核**：`FAILED` 必載 `errorDetail`（領域層強制）。

---

## 8. 外部依賴與降級

| 依賴 | 失敗處理（Stand-in） |
|---|---|
| Legacy ECS（來源）、各領域服務（目標） | 遷移為批次作業，失敗重跑（冪等）；不影響線上交易 |

---

## 9. 測試案例（對應 `TRP-001`）

- 波次執行／暫停／回退（`SYS-2.8`）
- 舊資料轉換與查詢（`SYS-3`）、欄位對映正確性

---

## 10. 已知限制（誠實揭露）

- ~~**`CC_MIG` 尚未建表**~~ → **已建**（`V2`；`SDD-001 §3.17` 已補 DDL 章節）。
- 波次狀態機待 `SDD-005` 正式化（本期值域以 DDL CHECK 為權威，**不提供**狀態轉換方法）。
- ~~`migration-service` 骨架與埠號尚未產生（規格先行）~~ → **已產生，埠號 8096，已啟動實測**。

**`CR-017` 執行後新增之限制（2026-09-22）**

1. **舊系統（Legacy ECS）資料源未接入** → 本期作業**不搬移任何帳務資料**；
   計數為**對映處理數**（`§5.1`）。
2. **`LEGACY_ACCOUNT_MAPPINGS` 之內容來源未實作**：對映由本地種子／人工建立；
   正式環境須由舊系統匯出或 API 接入。
3. **回退（`ROLLED_BACK`）未實作**：`SCS-015 §7` 原載「雙寫／比對期」之回退機制未落地。
4. **檢查點續跑未使用 `common-batch`**：本期以「作業狀態＋排程取件」表達，
   `common-batch` 尚未落地。
5. **波次狀態不隨作業自動推進**：波次與作業之連動（作業完成 → 波次完成）未實作。
6. **僅支援 `ACCOUNT` 之對映處理**：`CARD`／`BALANCE`／`POINTS`／`MERCHANT` 之對映
   表未建立（本期僅一通用對映表）。

---

## 11. 修訂記錄

| 版本 | 日期 | 修訂者 | 說明 |
|---|---|---|---|
| **1.2** | 2026-09-30 | WorkBuddy AI | **新增 §6.1「遷移作業清單端點」**（`GET /api/v1/migration-jobs?limit=N`）——解鎖 `SDD-007 §5.4.26` 桶 C「批次作業監控」（`SCS-020 §9.9`）。⚠️ **併載實測揭露**：該路徑原**僅有 `POST`**、`GET` 回 **404**。**新增 `MigrationJobSummary`**（含 `jobNo`／`startedAt`／`finishedAt`）——**不沿用既有 `MigrationJobView`**（該 DTO 為受理回應形狀、缺時間錨點），**不動既有已驗證之受理契約**。`limit` 預設 20／上限 100；無資料回空陣列非 404 |
| 1.0 | — | 開發團隊 | 初版（規格先行） |
| 1.1 | 2026-09-22 | WorkBuddy AI | **`CR-017` 裁定 E 執行完成（兩層 wave＋job）**——①**§5 改寫**為設計權威（三表欄位與值域、**兩層結構**、`§5.1` 本期 job 語意之誠實揭露）；②**§7** 新增實作要點（波次檢核、非同步、`dryRun` 語意、失敗可稽核）；③**§10** 移除兩項已解除限制、新增 6 項（資料源未接入／對映來源未實作／回退未實作／未用 `common-batch`／波次與作業未連動／僅支援 ACCOUNT）；④**補建本修訂記錄區**（原文件欠缺）。單測 17 例 0 失敗；真機 e2e 六項 |
