Skip to content

fix(ci): MoneyBox 遷移至新 endpoint 並修正欄位反轉與單位差 - #1051

Merged
s123104 merged 1 commit into
mainfrom
fix/moneybox-api-migration
Aug 26, 2026
Merged

fix(ci): MoneyBox 遷移至新 endpoint 並修正欄位反轉與單位差#1051
s123104 merged 1 commit into
mainfrom
fix/moneybox-api-migration

Conversation

@s123104

@s123104 s123104 commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

修什麼

MoneyBox 匯率抓取自 2026-08-22 起持續失敗,已停更 4 天。根因不是上游壞掉,是上游搬家。

以瀏覽器載入官網並攔截網路請求確認:

GET https://moneybox-exchange.com/api/rates           → 200  ← 官網現在打這個
     https://cems.moneybox.or.kr/api/cmd.php?cmd=C011  → 完全沒有被呼叫

舊端點半殘(仍回 200、base 持續更新,但 18/20 幣別 sell 恆為 0),所以純 API 探測分不出「壞掉」與「搬家」。

三個陷阱

1. buy / sell 語意相反

buyRate ≡ 舊 sell;新 sellRate ≡ 舊 buy 三重驗證:

驗證 證據
舊官網欄位 CAD 買入 1,015 = 舊 sell;賣出 1,028 = 舊 buy
新官網欄位 CAD 買入 1,004 = 新 buyRate;賣出 1,015 = 新 sellRate
價差方向 sell < buy;新 buyRate < sellRate — 店家低買高賣,順序一致

照字面把 sell 對到 sellRate 會取到價差的錯誤那一側。程式碼以具名常數表達這個反向對應,避免後人重蹈:

const NEW_TO_LEGACY_FIELD = Object.freeze({ sell: 'buyRate', buy: 'sellRate' });

2. JPY / IDR / VND 單位差 100 倍

新 API 一律 per-1,舊 API 對小面額幣別為 per-100。維持既有慣例以保歷史連續。

實跑結果與官網顯示完全一致:JPY 868/872、VND 5.1/5.6、IDR 7.8/9。

3. 上游移除 base / spbuy / spsell

新 API 每列僅 3 欄位currencyCode / buyRate / sellRate)。端點探查確認 /api/rates/latest/history/currencies/config/branches 皆 404。

  • base 改由兩側牌告價的算術中點推導
  • spbuy / spsell 一律 null不得以其他欄位推導填補

我自己引入又修掉的一個回歸

per-100 換算直接乘 100 會產生浮點雜訊:

8.72  × 100        → 872.0000000000001
0.056 × 100        → 5.6000000000000005
(42.15 + 42.3) / 2 → 42.224999999999994

這些值上游原本直接提供且乾淨,我的換算反而讓公開產物變糟。已改為以輸入位數推導輸出位數收斂(×100 減兩位、中點取較多者加一位),並加守門測試斷言輸出不得帶浮點尾數。

完整 decimal 算術遷移仍列 PR 2(見 PRD 049 §18.4)。這裡只做「不比原本更糟」的最小收斂。

驗證

對外零變化(PR 1 的核心驗收標準):

頂層欄位 新-舊:(無)      TWD 欄位 新-舊:(無)
頂層欄位 舊-新:(無)      TWD 欄位 舊-新:(無)
schemaVersion: 2.0  quoteUnit: KRW_PER_TWD
customerBuyForeignRate: 42.15  ← v2 語意欄位仍正確衍生

守門測試:新增 9 條,涵蓋映射方向、價差方向、per-100 還原、0 與非有限值、缺 currencyCode。

反向測試:把映射改回「照字面對接」後,2 條測試立即失敗 —— 守門確實會擋。

項目 結果
scripts typecheck 通過
pnpm test:root 7 檔 157 測試通過
實跑對比官網 19 幣別數值一致
pre-commit 六步 全綠

刻意不放進這個 PR

輪詢頻率調整。 上游標頭自宣 cache-control: max-age=14400(4 小時),我方 cron 每 5 分鐘(288 次/日,48 倍)。PRD §18.2 已裁決應改依上游快取契約並帶 If-Modified-Since

但那會改變對外更新節奏,與本 PR 的「對外零變化」驗收標準衝突,依 PRD §3.2「不混合關注點」原則另開 PR。

關聯

🤖 Generated with Claude Code

- 官網已完全改打 moneybox-exchange.com/api/rates,舊 cems 端點半殘
- 舊端點仍回 200 但 18 於 20 幣別 sell 恆 0,純 API 探測察覺不到搬家
- 新舊 buy 與 sell 語意相反:新 buyRate 等同舊 sell,反之亦然
- 以具名常數表達反向對應,避免後人照字面對接取到價差錯誤側
- 新 API 僅三欄位,base 改由兩側中點推導,spbuy 與 spsell 一律 null
- JPY 與 IDR 與 VND 由上游 per-1 還原為既有 per-100 慣例
- 換算以輸入位數推導輸出位數,避免乘除產生二進位浮點雜訊
- 0 與非有限值視為上游未報價,交由既有熔斷判定

測試:新增 9 條守門測試涵蓋映射方向、價差方向、per-100 還原與缺值處理;
以反轉映射的反向測試確認守門會擋;實跑驗證輸出與官網顯示一致且對外欄位
集合零變化;scripts typecheck 與 test:root 157 測試全綠

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Deprecation Warning: The deny-licenses option is deprecated for possible removal in the next major release. For more information, see issue 997.

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Scanned Files

None

@github-actions

Copy link
Copy Markdown
Contributor

✅ SEO 審計通過!所有 2026 標準驗證項目都符合要求。

  • ✅ Sitemap 2026 標準
  • ✅ Breadcrumb Schema
  • ✅ JSON-LD 結構化數據
  • ✅ 內部連結結構

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c7637d7954

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines 198 to +199
const data = await response.json();
const upstreamRows = data?.data?.rates;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 採用 publishedAt 保留上游資料齡

新回應已提供 data.publishedAt,但這裡只取 rates,後續仍以抓取當下時間產生 timestampupdateTime。當上游持續回傳 HTTP 200 與格式正確、但已停止更新的牌價時,完整性熔斷會通過,而每日 rollover 又會用新時間重寫快照;moneyboxRateService.ts 的 24 小時過期提示因此永遠把舊牌價視為最新資料。請驗證並使用 publishedAt 作為快照時間,而非以成功抓取時間冒充上游發布時間。

Useful? React with 👍 / 👎.

// MoneyBox 公開 API。
// 2026-08 上游遷移:官網已完全改打自有 endpoint,舊 cems.moneybox.or.kr 雖仍回 200
// 但 18/20 幣別的 sell 恆為 0(半殘)。以瀏覽器攔截官網請求確認新端點(PRD 049 §2.1)。
const MONEYBOX_API_URL = 'https://moneybox-exchange.com/api/rates';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 同步更新所有事故診斷中的 API 位址

端點改到 moneybox-exchange.com 後,失敗路徑仍把維運人員導向已退休的 cems.moneybox.or.kr:本腳本第 522 行的 troubleshooting,以及已檢查的 .github/workflows/update-moneybox-rates.yml 第 408、440 行之摘要與自動事故 issue 都仍輸出舊 URL。新端點故障時,告警會要求檢查那個仍可能回 200 的半殘舊服務,延誤定位;請讓這些診斷與此處的新端點同步。

Useful? React with 👍 / 👎.

@s123104
s123104 merged commit e06af11 into main Aug 26, 2026
19 checks passed
@s123104
s123104 deleted the fix/moneybox-api-migration branch August 26, 2026 15:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant