商品一起結帳,選項一起檢查

處理什麼問題?

多件商品放進同一台購物車,付款與配送方式必須符合每件商品的限制。

CalculatePayDeliveryMappingProcessor 找出共同支援的選項,再排除無法合法配對的金物流。

金流:每件商品都支援,才能留下

咖啡豆與餅乾一起結帳。✓ 代表支援,× 代表不支援。

商品信用卡ATMLINE Pay
咖啡豆×
餅乾×
商品交集保留排除排除

信用卡通過商品交集,仍須繼續套用購物車允許付款方式與配對限制。

物流:共同選宅配,各溫層保留自己的設定

假設同溫層商品已完成物流交集,且所有配送地區相同。

跨溫層比較配送類型與地區,不要求物流 Id 相同,也不表示商品要裝在同一包裹。

配對:有物流,還要有能搭配的金流

本例商品交集留下信用卡,以及宅配、超取付款兩種物流。

內部資料處理

Processor 呼叫 PayShippingMappingService.CalculatePayDeliveryMapping,讀取資料、計算交集,再寫回購物車。

結帳規則的兩大資料來源:商品金物流基準與全域合法配對設定
商品設定回答「各商品支援什麼」,全域 Mapping 回答「哪些金物流能搭配」。圖中 Dictionary 名稱為簡寫,完整欄位見下方來源表;快取未命中會查資料庫,取得的全域 Mapping 仍為空時才拋出 CartCacheExpired。

灰色為輸入、藍色為處理、綠色為可用結果、紅色為例外或無交集處理。實線表示順序,虛線表示資料供應。圖中省略排序與主子商品置換細節。

資料從哪裡來?查看來源與節點

下表購物車欄位位於 context.Data

來源讀取節點/用途
全域配對設定PayShippingMapping,定義金流與物流的合法搭配;查詢沒有以 ShopId 篩選。
商品金物流基準SalepageIdPayTypesDictionary[SalePageId]
SalepageIdDeliveryTypesDictionary[SalePageId]
參與交集的商品SalepageGroupList[…].SalepageList[…].PayTypeList
SalepageGroupList[…].SalepageList[…].DeliveryTypeList;已存在的贈品券商品也參與。
本次允許付款EnablePayProfileTypeDef
優惠碼與黑名單限制PromoCodeDispatch.PromoCodeInfo.PaymentTypes
BlacklistMemberAllowedPayProfileList

CartCreateProcessor.AssignSalePagePayShipping 先以 ShopId 與商品頁 Id 查詢 CartRepository.GetSalePagePayTypeAsyncGetSalePageDeliveryType,建立 Dictionary 並首次設定商品金物流。

兩支商品查詢分別呼叫 csp_GetSalePagePayTypeV2csp_GetSalePageDeliveryTypeV2。全域 Mapping 則由 GetPayShippingMappingAsync 透過快取取得,未命中才查 Repository。

還原與交集:查看條件、方法與比較鍵
處理規則
是否還原context.IsSkipSalepagePayDeliveryTypeInitial = false 才執行還原;為 true 時沿用目前商品清單。
商品還原ArrangePayDeliveryTypeToSalePage 在 Mapping Service 內從 Dictionary 重新指派商品的 PayTypeListDeliveryTypeList,不重新查商品設定。
同溫層物流GetSalePageGroupDeliveryType 先處理合併物流,再比對 Id + ShippingAreaId
跨溫層物流CalculateDeliveryTypeAsync 比對 ShippingProfileTypeDef + ShippingAreaId,保留各溫層明細。
商品金流CalculatePayTypePayProfileTypeDef 找共同付款類型,再套用允許清單與優惠碼規則。
合法配對CalculatePayShippingMapping 依全域設定刪減 CheckoutType 的兩份選項清單。
海外配送:聯集與交集的差別

CalculateOverseaDeliveryTypeAsync 讀取一般商品、已有贈品券商品,以及無交集清單中標記 OverseaNoIntersection 的商品物流,整理合併物流並去重、排序。

結果寫入 DeliveryTypeUnionList,供後續海外配送使用;它不是本輪共同可結帳的物流清單。

流程位置與原始碼對照

InitailPayShippingCalculate 中為第 4 步,在 RePayShippingCalculate 中為第 3 步。實際執行仍受入口條件與中斷影響。

主要來源:CalculatePayDeliveryMappingProcessor.ExecuteAsyncPayShippingMappingService.CalculatePayDeliveryMappingProcessorDefinitionCenter

全域 Mapping:金流與物流的合法配對設定

PayShippingMapping 定義付款類型與配送類型之間允許的搭配。這份設定供購物車計算使用,查詢沒有依本次商店的 ShopId 篩選。

一筆資料代表一組允許的搭配

付款類型配送類型這筆設定的意思
信用卡宅配允許信用卡搭配宅配。
信用卡海外配送允許同一付款類型搭配另一配送類型。

資料取得:先讀快取,未命中才查資料庫

CalculatePayDeliveryMappingProcessor.ExecuteAsync 呼叫 PayShippingMappingService.CalculatePayDeliveryMapping;Service 一開始便呼叫自己的 GetPayShippingMappingAsync 取得設定。

正常讀取路徑,cleanCache 預設為 false。快取由 IDataCacheService.GetMemoryOrRedisCacheDataAsync 統一處理。

來源節點用途與範圍
DataCacheService.GlobalSettingShopId使用全域設定識別值 -735268,不使用本次購物車的商店 Id。
Memory/Redis此查詢分別傳入 600 秒、3600 秒的快取期限。
PayShippingMappingRepository.GetPayShippingMappingAsync快取未命中時執行的資料來源查詢。
WebstoreReadOnlyDbContext讀取 PayShippingMappingPayProfile
快取識別:ServiceName、TypeName 與 Key

PayShippingMappingService.GetCacheKey 建立 ServiceName = PayShippingMappingTypeName = GetPayShippingMappingAsync-2016122117Key = All

這些是傳給快取服務的組成值。DataCacheService 會再加入全域設定 Id 與 locale,因此 All 不是完整實體快取鍵;全域也不表示跨所有 locale 共用同一鍵。

資料庫如何組成回傳欄位?

Repository 以 PayShippingMapping_PayProfileTypeDef = PayProfile_TypeDef JOIN 兩表,且 PayShippingMapping_ValidFlagPayProfile_ValidFlag 都必須為 true。

資料庫來源欄位PayShippingMappingEntity 欄位資料意義
PayShippingMapping.PayShippingMapping_PayProfileTypeDefPayProfileTypeDef配對的付款類型。
PayShippingMapping.PayShippingMapping_ShippingProfileTypeDefShippingProfileTypeDef配對的配送類型。
PayProfile.PayProfile_StatisticsTypeDefStatisticsTypeDef付款統計類型;本輪配對判斷未使用此欄。

取得後存在哪裡、交給誰?

節點/條件處理
CalculatePayDeliveryMapping 的區域變數 payShippingMappingList接收 List<PayShippingMappingEntity>,並未另外寫入 context.Data 的某個 Mapping 欄位。
清單為空立即拋出 CalculateException(CartCacheExpired),尚未進入商品交集計算。
清單有資料Service 保留此變數,待商品交集計算後,傳給 CalculatePayShippingMapping(cartEntity, payShippingMappingList) 使用。

商品頁本身支援哪些金流與物流?

查詢範圍:商店與商品頁 Id

CartCreateProcessor.ExecuteAsync 整理可結帳商品後,呼叫 AssignSalePagePayShipping。方法取出 salePageListCartSalePageId,以 Distinct 去重,再連同 context.ShopId 傳給 Repository。

資料CartRepository 方法Stored Procedure
商品金流GetSalePagePayTypeAsynccsp_GetSalePagePayTypeV2
商品物流GetSalePageDeliveryTypecsp_GetSalePageDeliveryTypeV2

兩支查詢都透過唯讀 DbContext 執行,參數是 @shopId 與逗號串接的 @salePageIds,查詢結果再以 SalePageId 分組。此處未套用前章全域 Mapping 的兩層快取。

資料如何進入購物車?

Dictionary 留在購物車內供後續依商品頁 Id 取用;重新指派時不在該方法重查 SP。

資料節點誰寫入、誰讀取
context.Data.SalepageIdPayTypesDictionary[SalePageId]AssignSalePagePayShipping 寫入金流基準清單;ArrangePayDeliveryTypeToSalePage 讀取。
context.Data.SalepageIdDeliveryTypesDictionary[SalePageId]同一寫入與讀取流程,保存物流明細。
salePageList[…].PayTypeListDeliveryTypeListAssignSalePagePayShippingCartSalePageId 首次指派。
context.Data.SalepageGroupList[…].SalepageList[…].PayTypeListDeliveryTypeListGroupCartSalePage 帶入商品群組;之後由交集計算方法直接讀取。

GroupCartSalePageCartSalePageId 轉成商品的 Id,並帶入兩份清單。建立新的 CartEntity 時,也會將先前的兩份 Dictionary 一併帶入。

例子:同一商品頁的不同 SKU 使用同一個查詢鍵

購物車商品商品頁 Id金流來源
咖啡豆,小包 SKU1001SalepageIdPayTypesDictionary[1001]
咖啡豆,大包 SKU1001SalepageIdPayTypesDictionary[1001]
馬克杯2002SalepageIdPayTypesDictionary[2002]
回傳欄位:金流類型與物流明細
Entity欄位/轉換
ShoppingCartClientPayTypeEntityPayProfileTypeDefStatisticsTypeDef 由 SP 結果同名欄位帶入。
ShoppingCartClientDeliveryTypeEntity 的識別與分類Id = ShopShippingTypeId;另含 ShippingProfileTypeDefShippingAreaIdTemperatureTypeDefMergeShippingTypeIdTypeName
物流費用與限制FeeOriginalFee 均來自 FeeFeeTypeDefOriginalFeeTypeDef 均來自 FeeTypeDef。另有 FeeTypeDefDescOverPriceMaxPriceLimitWeightLimit,以及 Rule = FeeRule
配送日期與排序IsEnableBookingPickupDateExcludeOfWeekSortShippingAreaSort,無值時為 0。

本次核對到 Cart Repository 的 SP 呼叫與結果模型,尚未展開兩支 SP 內部的資料表 JOIN,因此不將未查證的資料表列為底層來源。

Dictionary 是否就是未加工的資料庫原始設定?

AssignSalePagePayShipping 在寫入 Dictionary 前,依序執行自訂包裝顯示更新、全家點加金設定處理、代客下單金物流調整,以及物流多語系替換。因此它是 CartCreate 準備好的基準資料,可能已經過情境加工。

代客下單條件資料調整
IsAssistMode = false不進入代客下單替換分支。
FreeOfChargeAssistOrder付款清單替換為 FreeOfCharge,物流依零元訂單排除清單過濾。
其他代客下單付款清單只保留 CustomOfflinePayment
Mapping Service 如何讀取?缺 Key 與子商品如何處理?

IsSkipSalepagePayDeliveryTypeInitial = false 時,CalculatePayDeliveryMapping 呼叫自己的 ArrangePayDeliveryTypeToSalePage。這個方法以商品 Id 對兩份 Dictionary 執行 TryGetValue,成功才以 ToList() 重新指派商品清單;缺 Key 時沒有在這裡查 SP,也不在該分支清空既有清單。

旗標為 true 時跳過重新指派。重新指派使用的 ToList() 只建立新的清單容器,不代表其中每個金物流物件都被深層複製。

一般商品來自 SalepageGroupList,已有贈品券商品則從 SalePageGiftCouponGroupList 讀取。當 IsEnabledAddOnsPayDelivery 未啟用時,一般商品還原範圍會排除加購子商品;最後的 ReplaceSubSalePagePayShipping 再依設定,讓符合條件的加購或組合子商品沿用主商品清單。

後續 CalculatePayTypeCalculateDeliveryTypeAsync 讀取的是商品上的兩份清單。這些清單描述商品在目前處理階段的支援資料,尚不能直接當成整台購物車的最終選項。

所有參與商品共同接受哪些付款方式?

商品各自有付款清單,CalculatePayType 從中找出所有參與商品共同支援的付款類型,再套用本次購物車的限制。共同的範圍是商品之間,金流不按溫層分開計算。

案例:每件商品都支援,才是共同金流

商品頁信用卡ATMLINE Pay
咖啡豆 A
馬克杯 B×
商品交集保留移除保留

若本次 EnablePayProfileTypeDef 只允許信用卡與 ATM,上例的 LINE Pay 仍會被移除,輸出只剩信用卡。商品共同支援,是進入選項清單的第一道條件。

讀取來源與比較鍵

節點/方法用途
context.Data.SalepageGroupList[…].SalepageList[…].PayTypeList一般商品目前的付款清單;方法也追加 SalePageGiftCouponGroupList 中已有贈品券商品的付款資料。
PayProfileTypeDef依付款類型分組;保留分組筆數等於參與商品清單筆數的類型。
context.Data.EnablePayProfileTypeDef由前序 GetPayTypeIsAvaliableProcessor 準備,本方法用它再次過濾候選金流。
context.Data.PromoCodeDispatch.PromoCodeInfo.PaymentTypes優惠碼指定付款方式,有符合條件才縮減清單。
context.Data.CheckoutType.PayTypeList本方法最終寫入的付款候選清單,接著仍需經過全域 Mapping 配對。
優惠碼指定金流:有交集才縮減,完全不符交由後續處理
條件本方法行為
候選金流為空,或無優惠碼資料/未指定付款方式跳過優惠碼金流過濾。
指定付款方式與候選金流至少有一種相同只保留候選中符合指定的付款方式。
指定付款方式與候選金流完全不符保留這一步之前的候選清單;註解指定交由 CalculatePromoCodeProcessor 處理優惠碼無交集錯誤。

例如候選為信用卡、LINE Pay:優惠碼指定信用卡時留下信用卡;若只指定 ATM,此方法不因該條件直接清空候選金流。這不表示優惠碼已通過驗證。

程式中的計數、贈品券與排序細節

此實作使用 SelectMany → GroupBy(PayProfileTypeDef) → Count == salePageCount,未先依商品去重。將它理解為集合交集,前提是各商品清單中的同類型沒有重複;若資料重複,單純比較總筆數可能偏離集合交集語意。

前一個物流方法會把贈品券商品併入一般商品群組,而本方法又追加贈品券清單。因此計數基準是方法實際組出的 salePageList.Count,不能直接當作不重複商品頁 Id 數;這裡沿用程式行為,不另外宣稱已去重。

每個保留類型建立一筆 ShoppingCartClientPayTypeEntityStatisticsTypeDef 取該組第一筆。排序使用 PayProfileTypeDefComparer 與全域/商店 EventSort 設定。

各溫層都能使用哪些配送類型與地區?

CalculateDeliveryTypeAsync 先找同溫層商品的共同物流,再找各溫層共同具有的配送類型與地區。跨溫層通過後,仍保留各溫層自己的物流明細。

第一層:同溫層商品要有相同物流 Id 與地區

同溫層先整理合併物流,再依物流 Id 與配送地區取交集,範例保留宅配 101
同溫層比對物流 Id 與配送地區。圖中「子 Id 替換為主要 Id」為概念簡化;實際需確認 MergeShippingTypeId 指向的 Id 也存在,再由 MergeDeliveryType 整理,具體方向見本節「合併物流」範例。
常溫商品/地區 1宅配 #101超取付款 #201
咖啡豆 A
馬克杯 B×
常溫交集保留 #101移除 #201

GetSalePageGroupDeliveryType 在整理合併物流後,以 Id + ShippingAreaId 分組,保留筆數等於該溫層商品筆數的組合,並取各組第一筆明細。即使配送名稱都叫宅配,Id 或地區不同也不直接視為相同。

第二層:跨溫層改比配送類型與地區

常溫宅配 101 與冷凍宅配 301 的配送類型及地區相同,因此保留各自明細
跨溫層改比配送類型與地區:常溫 #101 與冷凍 #301 可以同時保留,不必使用相同物流 Id,也不代表合併成同一包裹。
溫層內交集結果物流 Id配送類型地區跨溫層結果
常溫#101宅配1保留
冷凍#301宅配1保留
冷凍#302宅配2移除,常溫沒有地區 2

兩個溫層都具有「宅配+地區 1」,所以 #101、#301 同時保留。這一步比較 ShippingProfileTypeDef + ShippingAreaId,不要求物流 Id 相同,也不表示常溫與冷凍商品裝在同一包裹。

資料讀寫節點

節點/方法用途
context.Data.SalepageGroupList主要商品群組;按溫層合併已有的 SalePageGiftCouponGroupList 後參與計算。
SalepageList[…].DeliveryTypeList每件商品目前的物流明細,是 GetSalePageGroupDeliveryType 的讀取來源。
salePageGroupDeliveryTypeList區域變數,保存各溫層的物流交集結果。
distinctSalePageGroupShippingTypeDefList區域變數,保存跨溫層共同的配送類型與地區組合。
context.Data.CheckoutType.DeliveryTypeList展開各溫層明細,只保留共同組合,排序後寫入,再依優惠碼與黑名單條件刪減。
合併物流:比較 Id 前會先整理

方法先收集具有 MergeShippingTypeId,且其指向 Id 也存在於該溫層物流中的設定,再由 MergeDeliveryType 整理每件商品清單。

假設設定商品原清單整理後
物流 #3 的 MergeShippingTypeId = 1#1#3
同一設定#1、#3#3
同一設定#1、#2#3、#2

以上條件成立時,#1 或 #3 都會被整理成合併設定 #3,再進行同溫層交集。這是本方法的比較用清單,不是單純忽略物流 Id。

交集後的限制:優惠碼與黑名單

優惠碼指定金流與黑名單限制的整理:有金流交集才縮減,特定折抵方式另有限制物流
左側是第 05 頁的金流規則:優惠碼指定金流與候選有交集時才縮減。右側是本節的物流限制:依特定折抵方式及黑名單條件移除不適用配送類型,兩者作用於不同清單。
交集後的物流限制:優惠碼與黑名單
來源/條件處理
PromoCodeDispatch.PromoCodeInfo.PaymentTypes 指定 StoreCreditGiftCardHamiPoint從該付款方式對應的 DeferredPaymentShippingProfileTypeDefList 找出不適用配送類型並移除;其他付款類型跳過此分支。
BlacklistMemberAllowedPayProfileList 非 null,且包含上述折抵方式從對應的不適用配送類型清單,扣除允許清單中也存在的值,再移除剩下的配送類型。不是把所有後付款物流一律移除。

對應限制清單分別定義於 CalculateCheckoutStoreCreditProcessorGiftCardSelectionHandlerCheckoutHamiPointCalculator。判斷逐項執行,符合多項時會連續過濾。配送類型為空白的項目在這兩段過濾中保留。

群組、計數與輸出邊界

salePageGroupList 直接引用 context.Data.SalepageGroupList。贈品券依溫層新增群組或追加商品,會修改 Context 中的群組,不是獨立複本。

同溫層使用分組總筆數與商品清單筆數比較,未全面先按商品去重。跨溫層則先在各組內對「配送類型+地區」執行 Distinct,再保留出現次數等於溫層群組數的組合。

保留明細依 ShippingProfileTypeDefComparer 排序,再依 ShippingAreaId 排序;Comparer 使用全域與商店 EventSort 設定。

輸出為 CheckoutType.DeliveryTypeList,後續全域 Mapping 與其他 Processor 仍可能刪減。海外聯集另由 CalculateOverseaDeliveryTypeAsync 寫入 DeliveryTypeUnionList,不屬於本章的物流交集輸出。

共同支援的選項,還要有合法搭配

商品共同接受某種付款、某種配送,不代表兩者可以搭配。CalculatePayShippingMapping 將交集後的選項套入全域 Mapping,留下有搭配對象的付款與配送選項。

三份輸入,各自回答不同問題

輸入節點前序來源讀取欄位
payShippingMappingList外層 CalculatePayDeliveryMapping 一開始取得的全域配對清單。PayProfileTypeDefShippingProfileTypeDef
context.Data.CheckoutType.PayTypeListCalculatePayType 完成商品交集與付款限制後的候選金流。PayProfileTypeDef
context.Data.CheckoutType.DeliveryTypeListCalculateDeliveryTypeAsync 完成交集與物流限制後的候選配送明細。ShippingProfileTypeDef

上表以 Processor 的 context.Data 表示購物車根節點;進入私有配對方法後,其 context 參數本身就是 CartEntity

案例:兩端都存在,這筆配對才有效

假設候選金流為「信用卡、ATM」,候選物流為「宅配、超取付款」。下表為教學假設設定,名稱是類型代稱。

全域 Mapping 配對付款端存在?配送端存在?本輪配對
信用卡 → 宅配保留
ATM → 宅配保留
超取付款金流 → 超取付款物流×排除
信用卡 → 海外配送×排除

實線為本輪有效配對,虛線為設定存在但本輪無效的配對。圖中聚焦候選物流的去留,省略候選中不存在的海外配送。

反向套回選項後,信用卡與 ATM 都有宅配可搭配,兩者保留;超取付款物流沒有有效連線,因此即使通過商品交集,仍會被移除。

實作:先篩配對,再依序刪減兩份清單

步驟操作結果位置
1.篩選配對兩個 Where 分別確認付款類型、配送類型存在於目前候選。區域變數 avaliblePayShippingMappingList,拼字沿用原始碼。
2.刪減金流RemoveAll 移除沒有出現在有效配對付款端的類型。直接修改 CheckoutType.PayTypeList
3.刪減物流RemoveAll 移除沒有出現在有效配對配送端的類型。直接修改 CheckoutType.DeliveryTypeList
比對鍵:為什麼不再比較物流 Id、溫層與地區?

這一步只使用 PayProfileTypeDefShippingProfileTypeDef。物流 Id、溫層、地區已在前段處理,本方法不以它們重新查詢 Mapping。

若宅配類型有有效配對,前段保留的常溫宅配 #101、冷凍宅配 #301 都能留下;此處不把兩筆明細合成一筆。StatisticsTypeDef 也不是本方法的配對鍵。

方法不重新查商品設定、不新增選項,也不把篩選後的配對表另外存入購物車。Where 結果是延遲列舉,未先以 ToList() 建立獨立配對快照。

每個選項有配對,不代表可以任意組合

另一個假設:只允許「信用卡 → 宅配」與「超取付款金流 → 超取付款物流」,而四個端點都在候選清單。此方法會保留全部四個選項,但不會因此允許「信用卡 → 超取付款物流」。

輸出清單表示每個選項至少存在一個合法搭配,不能將兩份清單直接解讀為所有組合都合法。這一步也沒有替消費者選定付款與配送方式。

配對後往哪裡走?

外層 Service 接著整理海外物流聯集,再檢查付款與配送清單是否皆非空。任一為空,就進入下一章的無交集商品搬移與中斷處理;皆有選項則更新提示並繼續後續計算。

「全域 Mapping 本身為空」在 Service 入口就會拋出 CartCacheExpired;「有全域設定,但本輪沒有有效配對」則會讓兩份候選清單被清空。這是兩個不同的處理情境。

DeliveryTypeUnionList:保留供後續查找的物流資料

CalculateOverseaDeliveryTypeAsync 整理商品物流聯集,寫入 context.Data.DeliveryTypeUnionList,供後續海外配送流程使用。方法名稱描述後續用途;此方法本身沒有只挑海外物流,也沒有判斷配送國家或地區。

為什麼交集之外,還需要聯集?

商品/資料集合宅配 #101海外配送 #401
商品 A
商品 B×
商品共同物流保留不在共同選項中
商品物流聯集保留保留,商品 A 有此設定
節點用途
CheckoutType.DeliveryTypeList商品交集、限制與 Mapping 配對後的本輪配送候選明細。
DeliveryTypeUnionList從商品物流重新收集並整理的聯集,保留供後續查找的物流設定,可能同時包含本地與海外物流。

聯集中存在 #401,只表示來源商品具有這筆物流資料,不表示商品 B 可以海外配送,也不表示整台購物車已通過海外配送檢查。

建立聯集與挑選海外物流,是兩個階段

實線表示階段內的處理順序,虛線表示資料供應。只有後續 GetOverseaDeliveryTypeList 才以 ShippingProfileTypeDef = Oversea 篩選。

聯集從哪些商品節點收集?

下表節點皆位於 context.Data。收集的是商品自己的 DeliveryTypeList,不是把 CheckoutType.DeliveryTypeList 複製成另一份清單。

來源節點納入條件
SalepageGroupList[…].SalepageList[…]目前一般商品群組內的商品。
SalePageGiftCouponGroupList[…].SalepageList[…]清單中已存在的贈品券商品。
UnMappingCheckoutSalepageList只納入 PayShippingIntersectionStatus = OverseaNoIntersection 的商品;不把所有無交集商品一律加回。
不只是塞值:合併、去重與排序規則

方法先展開來源商品的物流明細。若物流有 MergeShippingTypeId,且指向的 Id 也在收集清單內,便交給 MergeDeliveryType 整理比較用物流。

接著依 Id 分組,每組取第一筆,再以 ShippingProfileTypeDefComparerShippingAreaId 排序,最後用 ToList() 寫入 DeliveryTypeUnionList。此處去重鍵只有物流 Id,不是「類型+地區」的交集鍵。

ToList() 建立新的清單容器,不代表物流明細物件都被深層複製。這個方法也沒有用全域 Mapping 或付款清單重新篩選聯集。

後續誰讀取?寫出哪些結果?

GetOverseaShippingProcessor.ExecuteAsync 呼叫 ShippingAreaService.ArrangeOverseaShippingAreasAsync。其中的 GetOverseaDeliveryTypeListDeliveryTypeUnionList 挑出 Oversea 類型。

取得海外物流後,Service 依物流 Id 查找配送運費表與地區資料,整理配送地區聯集,再更新 CanOverseaShippingShippingAreaList。沒有海外物流時,會將海外可用狀態設為 false、清空地區清單並返回。

這是聯集資料的後續用途,不是 CalculateOverseaDeliveryTypeAsync 已經完成的工作;是否所有商品能送到所選地區仍須後續流程判斷。

在目前 Processor 中的執行位置

CalculatePayDeliveryMapping 先完成商品交集與 CalculatePayShippingMapping,再呼叫此方法建立聯集,之後才檢查候選金流、物流是否為空。因此即使建立了聯集,本輪仍可能在隨後的無交集判斷中要求中斷;不能假設後續海外 Processor 每次都一定執行。

結果與例外

先區分「配對設定缺失」與「商品計算後沒有共同選項」,兩者走不同處理分支。

付款選項

CheckoutType.PayTypeList

配送明細

CheckoutType.DeliveryTypeList

這兩份結果供後續運費、重量、金額門檻與預選計算使用,仍可能被調整。

無交集時,哪些商品與旗標會改變?

例如咖啡豆只支援信用卡、餅乾只支援 ATM,即使都能宅配,付款交集仍為空。

節點變化
UnMappingCheckoutSalepageList合併目前一般商品與贈品券商品,不只挑其中一件移除。
SalepageGroupListSalePageGiftCouponGroupList搬移商品後清空。
PayShippingIntersectionStatus無交集清單商品標記為 NoIntersection
context.IsInterruptedcontext.NeedReCalculateService 回傳無交集結果;Processor 保存 Redis 成功後,兩者設為 true。

這裡只提出重算需求,不直接重跑自己,後續由外層流程接手。

選項減少時,提示如何更新?

CheckPayShippingMapping 更新 CheckoutType.DisplayMessage。商品原有的金流種類數或配送類型與地區組合數,若與最後選項不同,就設定 OnlyFollowingPaymentAndShipping 對應提示。

出現提示可能只是選項減少,不表示一定無法結帳。無交集時商品已先搬移,也不能假設必定出現同一提示。