Upsert 與冪等
Upsert 與冪等
新增、更新、去重的判斷規則,以及唯一的例外
客戶、商品、規格(SKU)的建立端點都是同一套 upsert 規則:同一支端點,依你送出的欄位自動判斷這一筆該新增、更新,還是去重成更新既有的一筆。目的是讓 ERP 端可以重複推送同一份資料而不會產生重複紀錄,即使中途重試也一樣安全。
後台使用者是唯一的例外,規則不同,見本頁最後一節。
三條判斷規則
依序判斷:
- body 帶
id—— 一律視為更新該筆,直接依id找到那筆資料並更新,不會再去比對outerSysCode。 - body 沒帶
id,但outerSysCode命中既有資料 —— 改為更新命中的那一筆(這就是「冪等去重」:同一份outerSysCode重送多次,只會更新同一筆,不會越送越多筆)。 - 兩者都沒有命中 —— 新增一筆。
規格(SKU)的去重多一個限制:比對範圍限定在同一個商品之內。 判斷「這個 outerSysCode 是否已存在」時,比對的是「同一個 productId 底下」的既有規格,不同商品下即使 outerSysCode 完全相同,也不會互相匹配、也不會互相衝突。這是因為 SKU 代碼的唯一性本來就是以商品為範圍,不是全站唯一。
批次端點(/v1/customers/batch、/v1/products/batch、/v1/skus/batch)逐筆套用同一套規則,一筆一筆各自判斷新增或更新,互不影響;哪一筆成功、哪一筆失敗、是新增還是更新,都會在回應的逐筆結果裡分別回報。
三個分支各自長什麼樣子
以 POST /v1/customers 為例,假設 id=501、outerSysCode="ERP-C-1001" 的客戶已經存在:
分支 1 與分支 2 的差別只在有沒有帶 id——兩者最終都會落到「更新 501」,但走的是不同的判斷路徑;重送分支 2 的 body 一百次,也只會有一筆 id=501 的客戶被反覆更新,不會變成一百筆客戶。
部分合併:省略的欄位永遠不會被清空
更新(不管是依 id 直接更新,還是依 outerSysCode 去重出來的更新)一律是部分合併:只有你實際送出的欄位會被寫入,省略的欄位維持原本的值。 這代表:
- 只想改一個欄位時,body 裡只需要放那一個欄位,不需要把整筆資料重新送一次。
- 省略某個欄位不會把它清空或重設成預設值——例如更新客戶時只送
shippingAddress,客戶原本的name、shortName、cellPhone都會維持不變。 - 這條規則只適用於更新。新增時省略字串欄位視為空字串,省略數字欄位(例如規格的
price、sellPrice、quantityPrecision)視為 0,因為新增沒有「原本的值」可以保留。
實際看一次:客戶 id=501 目前長這樣——
只送 cellPhone 這一個欄位去更新:
更新後再查一次,只有 cellPhone 變了,其餘欄位(包含沒有出現在請求裡的 name、shortName、shippingAddress、internalUserId)原封不動:
status 不受這條規則影響,因為它本來就不是 upsert 請求裡的欄位——透過本 API 建立的客戶一律是 Active,狀態變更由後台維護。
PUT /{id}:路徑上的 id 永遠優先
PUT /v1/customers/{id}、PUT /v1/products/{id}、PUT /v1/skus/{id} 這三支路徑帶 id 的更新端點,路徑上的 id 永遠優先於 body 裡的 id——即使 body 另外指定了一個不同的 id,也一律以路徑上的為準,body 裡的 id 會被忽略。這是為了避免呼叫端不小心把更新導向另一筆資料:路徑已經明確表示「要更新哪一筆」,body 的 id 沒有機會覆蓋這個意圖。
這筆請求更新的是路徑上的 501,body 裡的 999 完全被忽略——不會去更新 id=999 那筆,也不會因為 999 不存在而報錯。回應裡的 id 也會是 501。
例外:POST /v1/internal-users 沒有去重
後台使用者的建立端點不遵循上面的三條規則——它永遠是新增,沒有 outerSysCode 去重更新這個分支。 即使送出的 outerSysCode 跟某個既有後台使用者相同,也不會被視為要更新那一筆,而是照樣嘗試新增一筆新的。
因為沒有去重機制,重複送出同一份資料會撞到帳號唯一性:同一租戶內 account 重複,會回傳 400 account_already_used,不會更新既有帳號,也不會被靜默忽略。如果你需要更新既有後台使用者的資料(例如改姓名、改角色),必須帶 id 呼叫 PUT /v1/internal-users/{id},POST 這支端點永遠走新增這條路。
會有這個例外,是因為後台使用者是登入帳號,account 本身就必須全租戶唯一,語意上不適合像客戶/商品/規格那樣用 outerSysCode 去重合併——重複的 account 代表操作本身有問題,需要呼叫端明確處理,而不是悄悄併進既有帳號。