分頁與篩選
分頁與篩選
清單端點的分頁形狀、篩選規則,以及需要特別注意的例外
大部分清單端點共用同一套分頁與篩選規則,這裡一次講清楚;各端點頁面只補充自己特有的篩選欄位。
回應形狀
清單端點的回應都是同一個外層:
data 是本頁的資料;totalCount 是符合篩選條件的總筆數,涵蓋所有頁,不受目前頁碼影響;currentPage 就是請求帶入的 page;pageSize 是請求帶入的 pageSize;totalPages 等於 totalCount 除以 pageSize 後無條件進位,沒有符合的資料時為 0。
page 與 pageSize
page 從 1 起算。pageSize 的範圍是 1 到 100,省略時預設為 100。
超出範圍會回傳 400,不會被靜默夾擠成合法值。 帶 pageSize=500 不會被悄悄改成 100 再繼續執行,而是直接拒絕整個請求:
這個設計是刻意的:如果超出範圍的 pageSize 被自動夾回合法值,一支寫死「送 pageSize=500、跑到 data 回傳筆數小於 500 就當作最後一頁」的掃描程式,會在完全沒有察覺的情況下把每一頁都當成 100 筆處理,進度計算全部跑偏,卻不會出現任何錯誤——資料看起來像是抓完了,其實漏了一大截。改成直接回 400,任何寫錯分頁參數的介接程式都會在第一次呼叫就發現問題,而不是在資料對不上帳的時候才發現。
updatedSince:增量同步用的篩選條件
支援增量同步的清單端點都能用 updatedSince 篩「異動時間等於或晚於此刻」的資料。這個邊界是含在內的:異動時間剛好等於你送出的那一刻,該筆資料也會被回傳,不是嚴格的「在此之後」。所以把上一輪掃到的最後一個異動時間直接當成下一輪的 updatedSince,交界處那幾筆會再出現一次——這是預期行為,你端依 id 覆寫即可,不要當成資料異常。
這個值必須是 ISO-8601 格式,並且帶明確的時區位移或 Z——2026-07-01T00:00:00Z 合法,2026-07-01T00:00:00(沒有位移、沒有 Z)會回傳 400 invalid_updated_since,不會被當成任何時區的預設猜測值。
放進網址時要留意百分號編碼:ISO-8601 的 +(例如 +08:00 時區)在 URL 裡有特殊意義,必須編碼成 %2B,否則會被解讀成空白字元;冒號通常也建議編碼成 %3A,避免部分用戶端函式庫或代理誤解析。例如:
或使用 UTC 的 Z 結尾,不含 +,可以省去這個顧慮:
updatedSince 進入簽章計算的方式跟其他 query 參數完全相同——canonical string 簽的是實際送出的、已經編碼過的那個字串(含 %3A、%2B),不是解碼後的可讀時間。細節見「驗證與簽章」的 path 與 query string 一節。
outerSysCode/name/barcode 的比對方式
支援這些欄位篩選的清單端點上,比對規則並不一致,混用時要特別注意:
outerSysCode、name:大小寫不敏感的子字串比對。 查A100也會比對到A1000,因為A1000這個字串裡包含A100。如果你只想找到剛好等於某個值的那一筆,篩選之後還是要在自己這端再比對一次完整字串。barcode:精確比對。 只有整段條碼完全相符才會命中,不是子字串比對,這一點與outerSysCode/name不同。
GET /v1/product-categories 的分頁是例外
商品分類樹的分頁單位是根分類,不是分類列表裡的每一筆節點,跟其他清單端點不一樣:
data最多回傳pageSize個根分類,每個根分類底下帶著它全部的子分類——子分類永遠不會被拆到下一頁,一個根分類要嘛整個出現在這一頁,要嘛完全不出現。totalCount算的是根分類的數量,不是分類總數(根分類數+子分類數)。- 因此一頁實際回傳的節點數是「
pageSize個根分類,加上它們各自帶的子分類」,不會剛好等於pageSize。
商品分類樹通常只有兩層、根分類數十個,預設分頁一次通常就能取回整棵樹,建議查一次後在你端快取,不必每次建立或更新商品前都重新查詢。
GET /v1/internal-roles 不分頁
後台角色清單沒有 data/totalCount/currentPage/pageSize/totalPages 這個分頁外層,回應就是一個 data 陣列,裡面是這個租戶可以指派的完整角色清單。一個租戶的角色數量少且固定,這支端點的用途是在建立後台使用者前查一次、在你端快取,不需要分頁機制。