@pisell/pisellos 0.0.567 → 0.0.569
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/index.js +6 -9
- package/dist/model/strategy/adapter/promotion/evaluator.js +3 -2
- package/dist/model/strategy/adapter/walletPass/evaluator.js +0 -2
- package/dist/model/strategy/adapter/walletPass/example.js +3 -26
- package/dist/model/strategy/adapter/walletPass/utils.js +7 -7
- package/dist/model/strategy/strategy-example.js +5 -19
- package/dist/modules/BaseModule.d.ts +2 -2
- package/dist/modules/BaseModule.js +4 -4
- package/dist/modules/BookingContext/index.d.ts +48 -0
- package/dist/modules/BookingContext/index.js +566 -0
- package/dist/modules/BookingContext/types.d.ts +102 -0
- package/dist/modules/BookingContext/types.js +51 -0
- package/dist/modules/BookingContext/utils/buildCacheItemFromOrderLine.d.ts +16 -0
- package/dist/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +302 -0
- package/dist/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.d.ts +8 -0
- package/dist/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.js +149 -0
- package/dist/modules/BookingContext/utils/cacheItemToBookingInput.d.ts +76 -0
- package/dist/modules/BookingContext/utils/cacheItemToBookingInput.js +249 -0
- package/dist/modules/BookingContext/utils/flexible.d.ts +28 -0
- package/dist/modules/BookingContext/utils/flexible.js +56 -0
- package/dist/modules/BookingContext/utils/formatResourceList.d.ts +8 -0
- package/dist/modules/BookingContext/utils/formatResourceList.js +38 -0
- package/dist/modules/BookingContext/utils/index.d.ts +11 -0
- package/dist/modules/BookingContext/utils/index.js +11 -0
- package/dist/modules/BookingContext/utils/noResource.d.ts +13 -0
- package/dist/modules/BookingContext/utils/noResource.js +77 -0
- package/dist/modules/BookingContext/utils/orderLineDisplay.d.ts +1 -0
- package/dist/modules/BookingContext/utils/orderLineDisplay.js +36 -0
- package/dist/modules/BookingContext/utils/productExtend.d.ts +72 -0
- package/dist/modules/BookingContext/utils/productExtend.js +339 -0
- package/dist/modules/BookingContext/utils/resourceErrors.d.ts +41 -0
- package/dist/modules/BookingContext/utils/resourceErrors.js +74 -0
- package/dist/modules/BookingContext/utils/resourceSelection.d.ts +7 -0
- package/dist/modules/BookingContext/utils/resourceSelection.js +24 -0
- package/dist/modules/BookingContext/utils/resources.d.ts +80 -0
- package/dist/modules/BookingContext/utils/resources.js +486 -0
- package/dist/modules/BookingContext/utils/serviceTimes.d.ts +55 -0
- package/dist/modules/BookingContext/utils/serviceTimes.js +151 -0
- package/dist/modules/BookingContext/utils/timeSlices.d.ts +42 -0
- package/dist/modules/BookingContext/utils/timeSlices.js +100 -0
- package/dist/modules/Cart/index.js +0 -3
- package/dist/modules/Cart/utils/cartProduct.js +26 -6
- package/dist/modules/Cart/utils/changePrice.js +27 -20
- package/dist/modules/Customer/index.d.ts +20 -0
- package/dist/modules/Customer/index.js +172 -83
- package/dist/modules/Customer/types.d.ts +2 -0
- package/dist/modules/Date/index.js +1 -5
- package/dist/modules/Discount/index.d.ts +2 -1
- package/dist/modules/Discount/index.js +37 -12
- package/dist/modules/Discount/types.d.ts +1 -0
- package/dist/modules/OpenData/index.d.ts +8 -0
- package/dist/modules/OpenData/index.js +35 -17
- package/dist/modules/Order/index.d.ts +449 -24
- package/dist/modules/Order/index.js +5437 -608
- package/dist/modules/Order/types.d.ts +693 -88
- package/dist/modules/Order/types.js +68 -1
- package/dist/modules/Order/utils/bundleDiscountHydration.d.ts +5 -0
- package/dist/modules/Order/utils/bundleDiscountHydration.js +144 -0
- package/dist/modules/Order/utils/manualProductDiscount.d.ts +106 -0
- package/dist/modules/Order/utils/manualProductDiscount.js +210 -0
- package/dist/modules/Order/utils/orderCollectionIdentity.d.ts +53 -0
- package/dist/modules/Order/utils/orderCollectionIdentity.js +405 -0
- package/dist/modules/Order/utils.d.ts +167 -18
- package/dist/modules/Order/utils.js +960 -165
- package/dist/modules/Payment/index.d.ts +10 -9
- package/dist/modules/Payment/index.js +508 -874
- package/dist/modules/Payment/types.d.ts +40 -16
- package/dist/modules/Payment/utils.d.ts +1 -1
- package/dist/modules/Payment/utils.js +4 -3
- package/dist/modules/Payment/walletpass.js +48 -34
- package/dist/modules/Product/types.d.ts +29 -7
- package/dist/modules/ProductList/index.d.ts +17 -13
- package/dist/modules/ProductList/index.js +184 -74
- package/dist/modules/ProductList/types.d.ts +17 -0
- package/dist/modules/Quotation/index.d.ts +22 -2
- package/dist/modules/Quotation/index.js +108 -42
- package/dist/modules/Quotation/types.d.ts +19 -0
- package/dist/modules/ResourcePlanner/index.d.ts +24 -0
- package/dist/modules/ResourcePlanner/index.js +181 -0
- package/dist/modules/ResourcePlanner/planner.d.ts +14 -0
- package/dist/modules/ResourcePlanner/planner.js +1069 -0
- package/dist/modules/ResourcePlanner/types.d.ts +227 -0
- package/dist/modules/ResourcePlanner/types.js +1 -0
- package/dist/modules/Rules/index.d.ts +8 -10
- package/dist/modules/Rules/index.js +219 -114
- package/dist/modules/Rules/types.d.ts +14 -3
- package/dist/modules/SalesSummary/index.d.ts +23 -26
- package/dist/modules/SalesSummary/index.js +152 -46
- package/dist/modules/SalesSummary/types.d.ts +9 -1
- package/dist/modules/SalesSummary/utils.d.ts +3 -22
- package/dist/modules/SalesSummary/utils.js +803 -163
- package/dist/modules/Schedule/getDateIsInSchedule.js +14 -7
- package/dist/modules/Schedule/index.d.ts +18 -5
- package/dist/modules/Schedule/index.js +78 -24
- package/dist/modules/Schedule/types.d.ts +2 -0
- package/dist/modules/Schedule/utils.js +0 -1
- package/dist/modules/Step/index.js +0 -1
- package/dist/modules/Summary/index.js +4 -3
- package/dist/modules/SurchargeList/index.d.ts +18 -0
- package/dist/modules/SurchargeList/index.js +185 -0
- package/dist/modules/SurchargeList/types.d.ts +15 -0
- package/dist/modules/SurchargeList/types.js +1 -0
- package/dist/modules/index.d.ts +3 -0
- package/dist/modules/index.js +4 -1
- package/dist/plugins/app.d.ts +2 -0
- package/dist/plugins/request.d.ts +1 -0
- package/dist/plugins/request.js +1 -4
- package/dist/plugins/window.js +2 -6
- package/dist/server/index.d.ts +462 -8
- package/dist/server/index.js +6137 -714
- package/dist/server/modules/floor-plan/index.d.ts +39 -0
- package/dist/server/modules/floor-plan/index.js +595 -0
- package/dist/server/modules/floor-plan/types.d.ts +43 -0
- package/dist/server/modules/floor-plan/types.js +13 -0
- package/dist/server/modules/index.d.ts +7 -2
- package/dist/server/modules/index.js +6 -0
- package/dist/server/modules/menu/index.js +32 -41
- package/dist/server/modules/order/index.d.ts +207 -14
- package/dist/server/modules/order/index.js +2326 -379
- package/dist/server/modules/order/types.d.ts +76 -5
- package/dist/server/modules/order/types.js +14 -1
- package/dist/server/modules/order/utils/filterBookings.d.ts +7 -1
- package/dist/server/modules/order/utils/filterBookings.js +118 -11
- package/dist/server/modules/order/utils/filterOrders.js +242 -14
- package/dist/server/modules/payment/index.d.ts +28 -0
- package/dist/server/modules/payment/index.js +305 -0
- package/dist/server/modules/payment/types.d.ts +9 -0
- package/dist/server/modules/payment/types.js +1 -0
- package/dist/server/modules/products/index.d.ts +105 -14
- package/dist/server/modules/products/index.js +1397 -540
- package/dist/server/modules/products/types.d.ts +34 -1
- package/dist/server/modules/products/types.js +4 -0
- package/dist/server/modules/quotation/index.d.ts +39 -0
- package/dist/server/modules/quotation/index.js +289 -86
- package/dist/server/modules/resource/index.d.ts +2 -3
- package/dist/server/modules/resource/index.js +105 -84
- package/dist/server/modules/schedule/index.d.ts +6 -4
- package/dist/server/modules/schedule/index.js +137 -114
- package/dist/server/test.json +713 -0
- package/dist/server/types.d.ts +2 -0
- package/dist/server/utils/product.js +2 -3
- package/dist/server/utils/small-ticket.d.ts +71 -0
- package/dist/server/utils/small-ticket.js +963 -0
- package/dist/solution/BaseSales/index.d.ts +378 -0
- package/dist/solution/BaseSales/index.js +4006 -0
- package/dist/solution/BaseSales/types.d.ts +394 -0
- package/dist/solution/BaseSales/types.js +40 -0
- package/dist/solution/BaseSales/utils/cartPromotion.d.ts +287 -0
- package/dist/solution/BaseSales/utils/cartPromotion.js +1450 -0
- package/dist/solution/BaseSales/utils/parseSalesResponse.d.ts +144 -0
- package/dist/solution/BaseSales/utils/parseSalesResponse.js +157 -0
- package/dist/solution/BaseSales/utils/quotationPrice.d.ts +18 -0
- package/dist/solution/BaseSales/utils/quotationPrice.js +250 -0
- package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.d.ts +63 -0
- package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +218 -0
- package/dist/solution/BaseSales/utils.d.ts +40 -0
- package/dist/solution/BaseSales/utils.js +263 -0
- package/dist/solution/BookingByStep/index.js +7 -59
- package/dist/solution/BookingByStep/utils/capacity.js +1 -11
- package/dist/solution/BookingByStep/utils/resources.js +1 -3
- package/dist/solution/BookingByStep/utils/timeslots.js +2 -43
- package/dist/solution/BookingTicket/index.d.ts +393 -24
- package/dist/solution/BookingTicket/index.js +2506 -445
- package/dist/solution/BookingTicket/types.d.ts +191 -6
- package/dist/solution/BookingTicket/types.js +83 -10
- package/dist/solution/BookingTicket/utils/addProductDecision.d.ts +117 -0
- package/dist/solution/BookingTicket/utils/addProductDecision.js +346 -0
- package/dist/solution/BookingTicket/utils/addTimeAvailability.d.ts +35 -0
- package/dist/solution/BookingTicket/utils/addTimeAvailability.js +95 -0
- package/dist/solution/BookingTicket/utils/bookingStatus.d.ts +77 -0
- package/dist/solution/BookingTicket/utils/bookingStatus.js +136 -0
- package/dist/solution/BookingTicket/utils/cartView.d.ts +29 -0
- package/dist/solution/BookingTicket/utils/cartView.js +209 -0
- package/dist/solution/BookingTicket/utils/exampleData.d.ts +123 -0
- package/dist/solution/BookingTicket/utils/exampleData.js +183 -0
- package/dist/solution/BookingTicket/utils/orderCustomer.d.ts +4 -0
- package/dist/solution/BookingTicket/utils/orderCustomer.js +31 -0
- package/dist/solution/BookingTicket/utils/resolveBestAddTimePlan.d.ts +185 -0
- package/dist/solution/BookingTicket/utils/resolveBestAddTimePlan.js +438 -0
- package/dist/solution/BookingTicket/utils/scan/applyGlobalScan.d.ts +36 -0
- package/dist/solution/BookingTicket/utils/scan/applyGlobalScan.js +400 -0
- package/dist/solution/BookingTicket/utils/scan/cloudSearch.d.ts +4 -0
- package/dist/solution/BookingTicket/utils/scan/cloudSearch.js +41 -1
- package/dist/solution/BookingTicket/utils/scan/formatGlobalScan.d.ts +32 -0
- package/dist/solution/BookingTicket/utils/scan/formatGlobalScan.js +56 -0
- package/dist/solution/BookingTicket/utils/scan/handleScan.d.ts +3 -1
- package/dist/solution/BookingTicket/utils/scan/handleScan.js +36 -34
- package/dist/solution/BookingTicket/utils/scan/index.d.ts +5 -0
- package/dist/solution/BookingTicket/utils/scan/index.js +26 -18
- package/dist/solution/BookingTicket/utils/scan/scanProductValues.d.ts +21 -0
- package/dist/solution/BookingTicket/utils/scan/scanProductValues.js +150 -0
- package/dist/solution/BookingTicket/utils/sessionCatalogStale.d.ts +23 -0
- package/dist/solution/BookingTicket/utils/sessionCatalogStale.js +78 -0
- package/dist/solution/BuyTickets/index.js +9 -12
- package/dist/solution/Checkout/index.d.ts +1 -0
- package/dist/solution/Checkout/index.js +199 -277
- package/dist/solution/Checkout/utils/index.js +8 -20
- package/dist/solution/RegisterAndLogin/config.js +2 -10
- package/dist/solution/RegisterAndLogin/index.js +30 -76
- package/dist/solution/Sales/index.d.ts +50 -5
- package/dist/solution/Sales/index.js +484 -92
- package/dist/solution/Sales/types.d.ts +26 -2
- package/dist/solution/ScanOrder/index.d.ts +27 -21
- package/dist/solution/ScanOrder/index.js +509 -454
- package/dist/solution/ScanOrder/types.d.ts +33 -11
- package/dist/solution/ScanOrder/types.js +4 -5
- package/dist/solution/ScanOrder/utils.d.ts +4 -1
- package/dist/solution/ScanOrder/utils.js +109 -34
- package/dist/solution/ShopDiscount/index.js +6 -11
- package/dist/solution/UnifiedBookingSales/index.d.ts +183 -0
- package/dist/solution/UnifiedBookingSales/index.js +1871 -0
- package/dist/solution/UnifiedBookingSales/types.d.ts +143 -0
- package/dist/solution/UnifiedBookingSales/types.js +11 -0
- package/dist/solution/VenueBooking/index.d.ts +34 -20
- package/dist/solution/VenueBooking/index.js +630 -505
- package/dist/solution/VenueBooking/sales-section-4-annotated.json +0 -2
- package/dist/solution/VenueBooking/utils/slotMerge.d.ts +6 -0
- package/dist/solution/VenueBooking/utils/slotMerge.js +9 -3
- package/dist/solution/index.d.ts +2 -0
- package/dist/solution/index.js +3 -1
- package/dist/types/index.d.ts +11 -1
- package/dist/utils/task.js +15 -18
- package/dist/utils/watch.js +0 -1
- package/lib/apis/picoding.js +2 -0
- package/lib/core/index.js +130 -131
- package/lib/effects/index.js +46 -57
- package/lib/index.js +92 -53
- package/lib/model/index.js +14 -21
- package/lib/model/strategy/adapter/dataVariant/adapter.js +38 -50
- package/lib/model/strategy/adapter/dataVariant/evaluator.js +162 -236
- package/lib/model/strategy/adapter/dataVariant/examples.js +229 -237
- package/lib/model/strategy/adapter/dataVariant/index.js +39 -34
- package/lib/model/strategy/adapter/dataVariant/type.js +57 -28
- package/lib/model/strategy/adapter/index.js +100 -64
- package/lib/model/strategy/adapter/itemRule/adapter.js +156 -144
- package/lib/model/strategy/adapter/itemRule/evaluator.js +36 -48
- package/lib/model/strategy/adapter/itemRule/examples.js +295 -225
- package/lib/model/strategy/adapter/itemRule/index.js +83 -57
- package/lib/model/strategy/adapter/itemRule/type.js +97 -36
- package/lib/model/strategy/adapter/promotion/adapter.js +84 -63
- package/lib/model/strategy/adapter/promotion/evaluator.js +383 -232
- package/lib/model/strategy/adapter/promotion/examples.js +139 -159
- package/lib/model/strategy/adapter/promotion/index.js +1 -49
- package/lib/model/strategy/adapter/promotion/type.js +199 -35
- package/lib/model/strategy/adapter/type.js +4 -16
- package/lib/model/strategy/adapter/walletPass/evaluator.js +180 -152
- package/lib/model/strategy/adapter/walletPass/example.js +217 -190
- package/lib/model/strategy/adapter/walletPass/index.js +75 -51
- package/lib/model/strategy/adapter/walletPass/locales.js +36 -58
- package/lib/model/strategy/adapter/walletPass/type.js +4 -16
- package/lib/model/strategy/adapter/walletPass/utils.js +654 -402
- package/lib/model/strategy/engine.js +270 -168
- package/lib/model/strategy/index.js +49 -34
- package/lib/model/strategy/strategy-example.js +243 -239
- package/lib/model/strategy/type.js +100 -40
- package/lib/modules/Account/index.js +39 -53
- package/lib/modules/Account/types.js +20 -33
- package/lib/modules/AccountList/index.js +116 -154
- package/lib/modules/AccountList/types.js +30 -31
- package/lib/modules/AccountList/utils.js +18 -30
- package/lib/modules/BaseModule.d.ts +2 -2
- package/lib/modules/BaseModule.js +26 -49
- package/lib/modules/BookingContext/index.d.ts +48 -0
- package/lib/modules/BookingContext/index.js +390 -0
- package/lib/modules/BookingContext/types.d.ts +102 -0
- package/lib/modules/BookingContext/types.js +48 -0
- package/lib/modules/BookingContext/utils/buildCacheItemFromOrderLine.d.ts +16 -0
- package/lib/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +286 -0
- package/lib/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.d.ts +8 -0
- package/lib/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.js +148 -0
- package/lib/modules/BookingContext/utils/cacheItemToBookingInput.d.ts +76 -0
- package/lib/modules/BookingContext/utils/cacheItemToBookingInput.js +242 -0
- package/lib/modules/BookingContext/utils/flexible.d.ts +28 -0
- package/lib/modules/BookingContext/utils/flexible.js +63 -0
- package/lib/modules/BookingContext/utils/formatResourceList.d.ts +8 -0
- package/lib/modules/BookingContext/utils/formatResourceList.js +35 -0
- package/lib/modules/BookingContext/utils/index.d.ts +11 -0
- package/lib/modules/BookingContext/utils/index.js +126 -0
- package/lib/modules/BookingContext/utils/noResource.d.ts +13 -0
- package/lib/modules/BookingContext/utils/noResource.js +78 -0
- package/lib/modules/BookingContext/utils/orderLineDisplay.d.ts +1 -0
- package/lib/modules/BookingContext/utils/orderLineDisplay.js +29 -0
- package/lib/modules/BookingContext/utils/productExtend.d.ts +72 -0
- package/lib/modules/BookingContext/utils/productExtend.js +330 -0
- package/lib/modules/BookingContext/utils/resourceErrors.d.ts +41 -0
- package/lib/modules/BookingContext/utils/resourceErrors.js +80 -0
- package/lib/modules/BookingContext/utils/resourceSelection.d.ts +7 -0
- package/lib/modules/BookingContext/utils/resourceSelection.js +24 -0
- package/lib/modules/BookingContext/utils/resources.d.ts +80 -0
- package/lib/modules/BookingContext/utils/resources.js +419 -0
- package/lib/modules/BookingContext/utils/serviceTimes.d.ts +55 -0
- package/lib/modules/BookingContext/utils/serviceTimes.js +161 -0
- package/lib/modules/BookingContext/utils/timeSlices.d.ts +42 -0
- package/lib/modules/BookingContext/utils/timeSlices.js +114 -0
- package/lib/modules/Cart/index.js +170 -124
- package/lib/modules/Cart/types.js +46 -51
- package/lib/modules/Cart/utils/cartAccount.js +40 -39
- package/lib/modules/Cart/utils/cartDate.js +56 -61
- package/lib/modules/Cart/utils/cartDiscount.js +28 -33
- package/lib/modules/Cart/utils/cartNote.js +27 -32
- package/lib/modules/Cart/utils/cartProduct.js +331 -244
- package/lib/modules/Cart/utils/cartRelationForms.js +29 -35
- package/lib/modules/Cart/utils/cartResource.js +72 -78
- package/lib/modules/Cart/utils/changePrice.js +37 -67
- package/lib/modules/Cart/utils/index.js +106 -48
- package/lib/modules/Customer/constants.js +13 -34
- package/lib/modules/Customer/index.d.ts +20 -0
- package/lib/modules/Customer/index.js +270 -154
- package/lib/modules/Customer/types.d.ts +2 -0
- package/lib/modules/Customer/types.js +38 -35
- package/lib/modules/Date/index.js +116 -115
- package/lib/modules/Date/types.js +16 -28
- package/lib/modules/Date/utils.js +174 -123
- package/lib/modules/Discount/index.d.ts +2 -1
- package/lib/modules/Discount/index.js +133 -113
- package/lib/modules/Discount/types.d.ts +1 -0
- package/lib/modules/Discount/types.js +9 -31
- package/lib/modules/Guests/index.js +30 -56
- package/lib/modules/Guests/types.js +23 -33
- package/lib/modules/Holder/index.js +209 -189
- package/lib/modules/Holder/types.js +4 -16
- package/lib/modules/Holder/utils.js +20 -49
- package/lib/modules/OpenData/index.d.ts +8 -0
- package/lib/modules/OpenData/index.js +81 -74
- package/lib/modules/OpenData/types.js +4 -16
- package/lib/modules/OpenData/utils.js +44 -78
- package/lib/modules/Order/index.d.ts +449 -24
- package/lib/modules/Order/index.js +3834 -489
- package/lib/modules/Order/types.d.ts +693 -88
- package/lib/modules/Order/types.js +78 -33
- package/lib/modules/Order/utils/bundleDiscountHydration.d.ts +5 -0
- package/lib/modules/Order/utils/bundleDiscountHydration.js +106 -0
- package/lib/modules/Order/utils/manualProductDiscount.d.ts +106 -0
- package/lib/modules/Order/utils/manualProductDiscount.js +207 -0
- package/lib/modules/Order/utils/orderCollectionIdentity.d.ts +53 -0
- package/lib/modules/Order/utils/orderCollectionIdentity.js +353 -0
- package/lib/modules/Order/utils.d.ts +167 -18
- package/lib/modules/Order/utils.js +1247 -345
- package/lib/modules/Payment/cash.js +20 -33
- package/lib/modules/Payment/cashRecommendationAlgorithm.js +145 -76
- package/lib/modules/Payment/eftpos.js +16 -30
- package/lib/modules/Payment/index.d.ts +10 -9
- package/lib/modules/Payment/index.js +542 -502
- package/lib/modules/Payment/mx51.js +1 -0
- package/lib/modules/Payment/types.d.ts +40 -16
- package/lib/modules/Payment/types.js +216 -107
- package/lib/modules/Payment/utils.d.ts +1 -1
- package/lib/modules/Payment/utils.js +53 -49
- package/lib/modules/Payment/walletpass.js +231 -203
- package/lib/modules/Product/index.js +51 -46
- package/lib/modules/Product/types.d.ts +29 -7
- package/lib/modules/Product/types.js +4 -16
- package/lib/modules/Product/utils.js +32 -33
- package/lib/modules/ProductList/index.d.ts +17 -13
- package/lib/modules/ProductList/index.js +160 -118
- package/lib/modules/ProductList/types.d.ts +17 -0
- package/lib/modules/ProductList/types.js +9 -31
- package/lib/modules/Quotation/index.d.ts +22 -2
- package/lib/modules/Quotation/index.js +123 -89
- package/lib/modules/Quotation/types.d.ts +19 -0
- package/lib/modules/Quotation/types.js +4 -16
- package/lib/modules/Resource/index.js +27 -59
- package/lib/modules/Resource/types.js +22 -32
- package/lib/modules/Resource/utils.js +30 -38
- package/lib/modules/ResourcePlanner/index.d.ts +24 -0
- package/lib/modules/ResourcePlanner/index.js +147 -0
- package/lib/modules/ResourcePlanner/planner.d.ts +14 -0
- package/lib/modules/ResourcePlanner/planner.js +895 -0
- package/lib/modules/ResourcePlanner/types.d.ts +227 -0
- package/lib/modules/ResourcePlanner/types.js +5 -0
- package/lib/modules/Rules/index.d.ts +8 -10
- package/lib/modules/Rules/index.js +1286 -894
- package/lib/modules/Rules/types.d.ts +14 -3
- package/lib/modules/Rules/types.js +17 -40
- package/lib/modules/SalesSummary/index.d.ts +23 -26
- package/lib/modules/SalesSummary/index.js +130 -71
- package/lib/modules/SalesSummary/types.d.ts +9 -1
- package/lib/modules/SalesSummary/types.js +4 -16
- package/lib/modules/SalesSummary/utils.d.ts +3 -22
- package/lib/modules/SalesSummary/utils.js +738 -250
- package/lib/modules/ScanOrderLogger/index.js +59 -79
- package/lib/modules/ScanOrderLogger/providers/feishu.js +54 -78
- package/lib/modules/ScanOrderLogger/providers/grafana.js +13 -36
- package/lib/modules/ScanOrderLogger/types.js +4 -16
- package/lib/modules/Schedule/getDateIsInSchedule.js +345 -171
- package/lib/modules/Schedule/index.d.ts +18 -5
- package/lib/modules/Schedule/index.js +139 -114
- package/lib/modules/Schedule/type.js +4 -16
- package/lib/modules/Schedule/types.d.ts +2 -0
- package/lib/modules/Schedule/types.js +4 -16
- package/lib/modules/Schedule/utils.js +433 -291
- package/lib/modules/Step/index.js +40 -52
- package/lib/modules/Step/tyeps.js +4 -16
- package/lib/modules/Summary/index.js +66 -70
- package/lib/modules/Summary/types.js +4 -16
- package/lib/modules/Summary/utils.js +782 -429
- package/lib/modules/SurchargeList/index.d.ts +18 -0
- package/lib/modules/SurchargeList/index.js +95 -0
- package/lib/modules/SurchargeList/types.d.ts +15 -0
- package/lib/modules/SurchargeList/types.js +5 -0
- package/lib/modules/index.d.ts +3 -0
- package/lib/modules/index.js +245 -57
- package/lib/plugins/app.d.ts +2 -0
- package/lib/plugins/app.js +4 -16
- package/lib/plugins/index.js +36 -25
- package/lib/plugins/request.d.ts +1 -0
- package/lib/plugins/request.js +137 -126
- package/lib/plugins/shopStore.js +4 -16
- package/lib/plugins/user.js +4 -16
- package/lib/plugins/window.js +171 -179
- package/lib/server/index.d.ts +462 -8
- package/lib/server/index.js +4668 -713
- package/lib/server/modules/floor-plan/index.d.ts +39 -0
- package/lib/server/modules/floor-plan/index.js +306 -0
- package/lib/server/modules/floor-plan/types.d.ts +43 -0
- package/lib/server/modules/floor-plan/types.js +19 -0
- package/lib/server/modules/index.d.ts +7 -2
- package/lib/server/modules/index.js +106 -59
- package/lib/server/modules/menu/index.js +146 -122
- package/lib/server/modules/menu/types.js +18 -31
- package/lib/server/modules/order/index.d.ts +207 -14
- package/lib/server/modules/order/index.js +1463 -316
- package/lib/server/modules/order/types.d.ts +76 -5
- package/lib/server/modules/order/types.js +117 -32
- package/lib/server/modules/order/utils/filterBookings.d.ts +7 -1
- package/lib/server/modules/order/utils/filterBookings.js +288 -146
- package/lib/server/modules/order/utils/filterOrders.js +318 -64
- package/lib/server/modules/payment/index.d.ts +28 -0
- package/lib/server/modules/payment/index.js +168 -0
- package/lib/server/modules/payment/types.d.ts +9 -0
- package/lib/server/modules/payment/types.js +5 -0
- package/lib/server/modules/products/index.d.ts +105 -14
- package/lib/server/modules/products/index.js +969 -383
- package/lib/server/modules/products/types.d.ts +34 -1
- package/lib/server/modules/products/types.js +44 -34
- package/lib/server/modules/quotation/index.d.ts +39 -0
- package/lib/server/modules/quotation/index.js +220 -77
- package/lib/server/modules/quotation/types.js +21 -31
- package/lib/server/modules/resource/index.d.ts +2 -3
- package/lib/server/modules/resource/index.js +269 -214
- package/lib/server/modules/resource/types.js +42 -33
- package/lib/server/modules/schedule/index.d.ts +6 -4
- package/lib/server/modules/schedule/index.js +158 -132
- package/lib/server/modules/schedule/types.js +14 -21
- package/lib/server/modules/schedule/utils.js +334 -163
- package/lib/server/test.json +713 -0
- package/lib/server/types.d.ts +2 -0
- package/lib/server/types.js +4 -16
- package/lib/server/utils/index.js +25 -23
- package/lib/server/utils/product.js +197 -140
- package/lib/server/utils/schedule.js +34 -41
- package/lib/server/utils/small-ticket.d.ts +71 -0
- package/lib/server/utils/small-ticket.js +926 -0
- package/lib/server/utils/time.js +40 -49
- package/lib/solution/BaseSales/index.d.ts +378 -0
- package/lib/solution/BaseSales/index.js +2367 -0
- package/lib/solution/BaseSales/types.d.ts +394 -0
- package/lib/solution/BaseSales/types.js +47 -0
- package/lib/solution/BaseSales/utils/cartPromotion.d.ts +287 -0
- package/lib/solution/BaseSales/utils/cartPromotion.js +1155 -0
- package/lib/solution/BaseSales/utils/parseSalesResponse.d.ts +144 -0
- package/lib/solution/BaseSales/utils/parseSalesResponse.js +140 -0
- package/lib/solution/BaseSales/utils/quotationPrice.d.ts +18 -0
- package/lib/solution/BaseSales/utils/quotationPrice.js +231 -0
- package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.d.ts +63 -0
- package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +215 -0
- package/lib/solution/BaseSales/utils.d.ts +40 -0
- package/lib/solution/BaseSales/utils.js +259 -0
- package/lib/solution/BookingByStep/index.js +1530 -1343
- package/lib/solution/BookingByStep/types.js +40 -99
- package/lib/solution/BookingByStep/utils/capacity.js +192 -160
- package/lib/solution/BookingByStep/utils/products.js +42 -52
- package/lib/solution/BookingByStep/utils/resources.js +527 -330
- package/lib/solution/BookingByStep/utils/stock.js +65 -44
- package/lib/solution/BookingByStep/utils/timeslots.js +129 -131
- package/lib/solution/BookingTicket/index.d.ts +393 -24
- package/lib/solution/BookingTicket/index.js +1459 -160
- package/lib/solution/BookingTicket/types.d.ts +191 -6
- package/lib/solution/BookingTicket/types.js +101 -66
- package/lib/solution/BookingTicket/utils/addProductDecision.d.ts +117 -0
- package/lib/solution/BookingTicket/utils/addProductDecision.js +353 -0
- package/lib/solution/BookingTicket/utils/addTimeAvailability.d.ts +35 -0
- package/lib/solution/BookingTicket/utils/addTimeAvailability.js +94 -0
- package/lib/solution/BookingTicket/utils/bookingStatus.d.ts +77 -0
- package/lib/solution/BookingTicket/utils/bookingStatus.js +136 -0
- package/lib/solution/BookingTicket/utils/cartView.d.ts +29 -0
- package/lib/solution/BookingTicket/utils/cartView.js +200 -0
- package/lib/solution/BookingTicket/utils/exampleData.d.ts +123 -0
- package/lib/solution/BookingTicket/utils/exampleData.js +185 -0
- package/lib/solution/BookingTicket/utils/orderCustomer.d.ts +4 -0
- package/lib/solution/BookingTicket/utils/orderCustomer.js +35 -0
- package/lib/solution/BookingTicket/utils/resolveBestAddTimePlan.d.ts +185 -0
- package/lib/solution/BookingTicket/utils/resolveBestAddTimePlan.js +387 -0
- package/lib/solution/BookingTicket/utils/scan/applyGlobalScan.d.ts +36 -0
- package/lib/solution/BookingTicket/utils/scan/applyGlobalScan.js +220 -0
- package/lib/solution/BookingTicket/utils/scan/cloudSearch.d.ts +4 -0
- package/lib/solution/BookingTicket/utils/scan/cloudSearch.js +85 -80
- package/lib/solution/BookingTicket/utils/scan/formatGlobalScan.d.ts +32 -0
- package/lib/solution/BookingTicket/utils/scan/formatGlobalScan.js +64 -0
- package/lib/solution/BookingTicket/utils/scan/handleScan.d.ts +3 -1
- package/lib/solution/BookingTicket/utils/scan/handleScan.js +84 -80
- package/lib/solution/BookingTicket/utils/scan/index.d.ts +5 -0
- package/lib/solution/BookingTicket/utils/scan/index.js +103 -95
- package/lib/solution/BookingTicket/utils/scan/scanCache.js +90 -43
- package/lib/solution/BookingTicket/utils/scan/scanProductValues.d.ts +21 -0
- package/lib/solution/BookingTicket/utils/scan/scanProductValues.js +115 -0
- package/lib/solution/BookingTicket/utils/sessionCatalogStale.d.ts +23 -0
- package/lib/solution/BookingTicket/utils/sessionCatalogStale.js +83 -0
- package/lib/solution/BuyTickets/index.js +67 -70
- package/lib/solution/BuyTickets/types.js +22 -38
- package/lib/solution/Checkout/index.d.ts +1 -0
- package/lib/solution/Checkout/index.js +1461 -1169
- package/lib/solution/Checkout/types.js +91 -61
- package/lib/solution/Checkout/utils/index.js +228 -159
- package/lib/solution/PlaceOrder/index.js +55 -0
- package/lib/solution/RegisterAndLogin/config.js +495 -470
- package/lib/solution/RegisterAndLogin/index.js +633 -630
- package/lib/solution/RegisterAndLogin/types.js +189 -76
- package/lib/solution/RegisterAndLogin/utils.js +183 -125
- package/lib/solution/Sales/index.d.ts +50 -5
- package/lib/solution/Sales/index.js +495 -220
- package/lib/solution/Sales/types.d.ts +26 -2
- package/lib/solution/Sales/types.js +27 -33
- package/lib/solution/ScanOrder/index.d.ts +27 -21
- package/lib/solution/ScanOrder/index.js +908 -904
- package/lib/solution/ScanOrder/types.d.ts +33 -11
- package/lib/solution/ScanOrder/types.js +33 -34
- package/lib/solution/ScanOrder/utils.d.ts +4 -1
- package/lib/solution/ScanOrder/utils.js +457 -344
- package/lib/solution/ShopDiscount/index.js +230 -236
- package/lib/solution/ShopDiscount/types.js +15 -37
- package/lib/solution/ShopDiscount/utils.js +309 -172
- package/lib/solution/UnifiedBookingSales/index.d.ts +183 -0
- package/lib/solution/UnifiedBookingSales/index.js +1050 -0
- package/lib/solution/UnifiedBookingSales/types.d.ts +143 -0
- package/lib/solution/UnifiedBookingSales/types.js +14 -0
- package/lib/solution/VenueBooking/index.d.ts +34 -20
- package/lib/solution/VenueBooking/index.js +982 -950
- package/lib/solution/VenueBooking/sales-section-4-annotated.json +0 -2
- package/lib/solution/VenueBooking/types.js +20 -40
- package/lib/solution/VenueBooking/utils/dateSummary.js +22 -54
- package/lib/solution/VenueBooking/utils/resource.js +31 -54
- package/lib/solution/VenueBooking/utils/slotMerge.d.ts +6 -0
- package/lib/solution/VenueBooking/utils/slotMerge.js +96 -108
- package/lib/solution/VenueBooking/utils/smartPricing.js +112 -100
- package/lib/solution/VenueBooking/utils/timeSlot.js +133 -132
- package/lib/solution/VenueBooking/utils.js +130 -67
- package/lib/solution/index.d.ts +2 -0
- package/lib/solution/index.js +124 -37
- package/lib/store/createStore.js +32 -29
- package/lib/types/index.d.ts +11 -1
- package/lib/types/index.js +4 -16
- package/lib/utils/task.js +36 -36
- package/lib/utils/watch.js +81 -47
- package/package.json +2 -1
- package/dist/solution/Checkout/appointmentDemo.json +0 -1
- package/lib/solution/Checkout/appointmentDemo.json +0 -1
|
@@ -1,85 +1,116 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
var __export = (target, all) => {
|
|
6
|
-
for (var name in all)
|
|
7
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
8
|
-
};
|
|
9
|
-
var __copyProps = (to, from, except, desc) => {
|
|
10
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
-
for (let key of __getOwnPropNames(from))
|
|
12
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
-
}
|
|
15
|
-
return to;
|
|
16
|
-
};
|
|
17
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
18
|
-
|
|
19
|
-
// src/server/modules/products/index.ts
|
|
20
|
-
var products_exports = {};
|
|
21
|
-
__export(products_exports, {
|
|
22
|
-
ProductsModule: () => ProductsModule
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
23
5
|
});
|
|
24
|
-
|
|
25
|
-
var
|
|
26
|
-
var
|
|
27
|
-
var
|
|
28
|
-
var
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
6
|
+
exports.ProductsModule = void 0;
|
|
7
|
+
var _BaseModule = require("../../../modules/BaseModule");
|
|
8
|
+
var _Quotation = require("../../../modules/Quotation");
|
|
9
|
+
var _types = require("./types");
|
|
10
|
+
var _product = require("../../utils/product");
|
|
11
|
+
/**
|
|
12
|
+
* IndexDB 存储名称
|
|
13
|
+
*/
|
|
14
|
+
const INDEXDB_STORE_NAME = 'products';
|
|
15
|
+
/** 商品同步防抖时间(毫秒) */
|
|
16
|
+
const PRODUCT_SYNC_DEBOUNCE_MS = 10_000;
|
|
17
|
+
/** silentRefresh 最小间隔,避免 preload 后立即全量重拉 */
|
|
18
|
+
const PRODUCT_SILENT_REFRESH_MIN_INTERVAL_MS = 30_000;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Products 模块 - 用于获取和管理完整的商品详细数据
|
|
22
|
+
* 相比 ProductList 模块,Products 会获取所有详细信息并缓存
|
|
23
|
+
*/
|
|
24
|
+
class ProductsModule extends _BaseModule.BaseModule {
|
|
25
|
+
defaultName = 'products';
|
|
26
|
+
defaultVersion = '1.0.0';
|
|
27
|
+
store;
|
|
28
|
+
request;
|
|
29
|
+
dbManager; // IndexDBManager 实例
|
|
30
|
+
logger; // LoggerManager 实例
|
|
31
|
+
otherParams = {};
|
|
32
|
+
|
|
33
|
+
// 商品价格缓存:Map<日期, 应用了价格的商品列表>
|
|
34
|
+
productsPriceCache = new Map();
|
|
35
|
+
|
|
36
|
+
// 缓存配置
|
|
37
|
+
CACHE_MAX_DAYS = 7; // 最多缓存7天
|
|
38
|
+
|
|
39
|
+
// 商品格式化器列表
|
|
40
|
+
formatters = [];
|
|
41
|
+
|
|
42
|
+
// 是否已注册内置商品格式化器
|
|
43
|
+
isBuiltinFormatterRegistered = false;
|
|
44
|
+
// 旧报价单格式化器默认关闭,仅显式开启时作为兼容兜底注册
|
|
45
|
+
isLegacyPriceFormatterEnabled = false;
|
|
46
|
+
|
|
47
|
+
// 普通层 QuotationModule 桥接器:本地计算报价单价格,替代 /product/query/price
|
|
48
|
+
quotationPriceBridge;
|
|
49
|
+
quotationBridgeChannel;
|
|
50
|
+
quotationBridgeLoadedKey = '';
|
|
51
|
+
quotationBridgeRefreshPromise;
|
|
52
|
+
quotationBridgeRefreshKey;
|
|
53
|
+
quotationScheduleCache = new Map();
|
|
54
|
+
quotationScheduleCacheSource = null;
|
|
55
|
+
quotationScheduleCacheLoaded = false;
|
|
56
|
+
|
|
57
|
+
// ---- 商品数据同步相关 ----
|
|
58
|
+
productDataSource;
|
|
59
|
+
pendingSyncMessages = [];
|
|
60
|
+
syncTimer;
|
|
61
|
+
/** 最近一次 SSE 全量拉取完成时间 */
|
|
62
|
+
lastServerFullFetchAtMs = 0;
|
|
63
|
+
/** 最近一次 preload 完成时间(含 IndexDB 命中) */
|
|
64
|
+
lastPreloadCompletedAtMs = 0;
|
|
65
|
+
/** IndexDB 写入串行队列,避免快照与 patch 并发交错 */
|
|
66
|
+
productIndexDBSaveQueue = Promise.resolve();
|
|
67
|
+
|
|
68
|
+
/** 最近一次智能定价评估提取的 schedule 变价时间点(HH:mm),供 Server 订阅定时刷新使用 */
|
|
69
|
+
lastScheduleTimePoints = [];
|
|
32
70
|
constructor(name, version) {
|
|
33
71
|
super(name, version);
|
|
34
|
-
this.defaultName = "products";
|
|
35
|
-
this.defaultVersion = "1.0.0";
|
|
36
|
-
// LoggerManager 实例
|
|
37
|
-
this.otherParams = {};
|
|
38
|
-
// 商品价格缓存:Map<日期, 应用了价格的商品列表>
|
|
39
|
-
this.productsPriceCache = /* @__PURE__ */ new Map();
|
|
40
|
-
// 缓存配置
|
|
41
|
-
this.CACHE_MAX_DAYS = 7;
|
|
42
|
-
// 最多缓存7天
|
|
43
|
-
// 商品格式化器列表
|
|
44
|
-
this.formatters = [];
|
|
45
|
-
// 是否已注册内置价格格式化器
|
|
46
|
-
this.isPriceFormatterRegistered = false;
|
|
47
|
-
this.pendingSyncMessages = [];
|
|
48
72
|
}
|
|
49
73
|
async initialize(core, options) {
|
|
50
|
-
var _a;
|
|
51
74
|
this.core = core;
|
|
52
75
|
this.store = options.store;
|
|
53
76
|
this.otherParams = options.otherParams || {};
|
|
54
|
-
if (Array.isArray(
|
|
77
|
+
if (Array.isArray(options.initialState?.list)) {
|
|
55
78
|
this.store.list = options.initialState.list;
|
|
79
|
+
// 同步更新 Map 缓存
|
|
56
80
|
this.syncProductsMap();
|
|
57
|
-
this.core.effects.emit(
|
|
81
|
+
this.core.effects.emit(_types.ProductsHooks.onProductsChanged, this.store.list);
|
|
58
82
|
} else {
|
|
59
83
|
this.store.list = [];
|
|
60
|
-
this.store.map =
|
|
84
|
+
this.store.map = new Map();
|
|
61
85
|
}
|
|
62
|
-
this.request = core.getPlugin(
|
|
63
|
-
|
|
86
|
+
this.request = core.getPlugin('request');
|
|
87
|
+
|
|
88
|
+
// 获取并存储 dbManager 和 logger
|
|
89
|
+
const appPlugin = core.getPlugin('app');
|
|
64
90
|
if (appPlugin) {
|
|
65
91
|
const app = appPlugin.getApp();
|
|
66
92
|
this.dbManager = app.dbManager;
|
|
67
93
|
this.logger = app.logger;
|
|
68
|
-
if (this.dbManager) {
|
|
69
|
-
console.
|
|
70
|
-
} else {
|
|
71
|
-
console.warn("[Products] IndexDB Manager 未找到");
|
|
94
|
+
if (this.dbManager) {} else {
|
|
95
|
+
console.warn('[Products] IndexDB Manager 未找到');
|
|
72
96
|
}
|
|
73
97
|
}
|
|
74
|
-
|
|
98
|
+
|
|
99
|
+
// 注册内置商品格式化器;智能定价默认替代报价单价格逻辑
|
|
100
|
+
this.registerBuiltinProductFormatters();
|
|
101
|
+
|
|
102
|
+
// 初始化 ProductDataSource 实例
|
|
75
103
|
this.initProductDataSource();
|
|
104
|
+
|
|
105
|
+
// 初始化商品数据同步(pubsub 订阅)
|
|
76
106
|
this.setupProductSync();
|
|
77
|
-
this.logInfo(
|
|
107
|
+
this.logInfo('模块初始化完成', {
|
|
78
108
|
hasDbManager: !!this.dbManager,
|
|
79
109
|
hasLogger: !!this.logger,
|
|
80
110
|
initialProductCount: this.store.list.length
|
|
81
111
|
});
|
|
82
112
|
}
|
|
113
|
+
|
|
83
114
|
/**
|
|
84
115
|
* 记录信息日志
|
|
85
116
|
* @param title 日志标题
|
|
@@ -89,14 +120,16 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
89
120
|
try {
|
|
90
121
|
if (this.logger) {
|
|
91
122
|
this.logger.addLog({
|
|
92
|
-
type:
|
|
123
|
+
type: 'info',
|
|
93
124
|
title: `[ProductsModule] ${title}`,
|
|
94
125
|
metadata: metadata || {}
|
|
95
126
|
});
|
|
96
127
|
}
|
|
97
128
|
} catch {
|
|
129
|
+
// 日志记录失败不影响主流程
|
|
98
130
|
}
|
|
99
131
|
}
|
|
132
|
+
|
|
100
133
|
/**
|
|
101
134
|
* 记录警告日志
|
|
102
135
|
* @param title 日志标题
|
|
@@ -106,14 +139,16 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
106
139
|
try {
|
|
107
140
|
if (this.logger) {
|
|
108
141
|
this.logger.addLog({
|
|
109
|
-
type:
|
|
142
|
+
type: 'warning',
|
|
110
143
|
title: `[ProductsModule] ${title}`,
|
|
111
144
|
metadata: metadata || {}
|
|
112
145
|
});
|
|
113
146
|
}
|
|
114
147
|
} catch {
|
|
148
|
+
// 日志记录失败不影响主流程
|
|
115
149
|
}
|
|
116
150
|
}
|
|
151
|
+
|
|
117
152
|
/**
|
|
118
153
|
* 记录错误日志
|
|
119
154
|
* @param title 日志标题
|
|
@@ -123,56 +158,50 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
123
158
|
try {
|
|
124
159
|
if (this.logger) {
|
|
125
160
|
this.logger.addLog({
|
|
126
|
-
type:
|
|
161
|
+
type: 'error',
|
|
127
162
|
title: `[ProductsModule] ${title}`,
|
|
128
163
|
metadata: metadata || {}
|
|
129
164
|
});
|
|
130
165
|
}
|
|
131
166
|
} catch {
|
|
167
|
+
// 日志记录失败不影响主流程
|
|
132
168
|
}
|
|
133
169
|
}
|
|
170
|
+
|
|
134
171
|
/**
|
|
135
172
|
* 加载商品价格(原始方法,不带缓存)
|
|
136
173
|
* @private
|
|
137
174
|
*/
|
|
138
175
|
async loadProductsPrice({
|
|
139
176
|
ids = [],
|
|
140
|
-
|
|
141
|
-
|
|
177
|
+
schedule_date,
|
|
178
|
+
schedule_datetime,
|
|
179
|
+
customer_id
|
|
142
180
|
}) {
|
|
143
|
-
|
|
144
|
-
this.logInfo("开始加载商品价格", {
|
|
181
|
+
this.logInfo('开始加载商品价格', {
|
|
145
182
|
productCount: ids.length,
|
|
146
183
|
schedule_date,
|
|
184
|
+
schedule_datetime,
|
|
147
185
|
customer_id
|
|
148
186
|
});
|
|
149
187
|
const startTime = Date.now();
|
|
188
|
+
const datetime = schedule_datetime || schedule_date;
|
|
150
189
|
try {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
customer_id,
|
|
156
|
-
schedule_date
|
|
157
|
-
},
|
|
158
|
-
{
|
|
159
|
-
cache: {
|
|
160
|
-
mode: import_plugins.RequestModeENUM.REMOTE_LOCAL,
|
|
161
|
-
type: "memory"
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
);
|
|
190
|
+
await this.ensureQuotationBridgeReady(customer_id);
|
|
191
|
+
const idSet = new Set(ids);
|
|
192
|
+
const products = this.getProductsRef().filter(product => idSet.has(product.id));
|
|
193
|
+
const priceData = products.map(product => this.buildPriceDataFromQuotation(product, datetime, customer_id));
|
|
165
194
|
const duration = Date.now() - startTime;
|
|
166
|
-
this.logInfo(
|
|
195
|
+
this.logInfo('加载商品价格成功', {
|
|
167
196
|
productCount: ids.length,
|
|
168
|
-
priceDataCount:
|
|
197
|
+
priceDataCount: priceData.length,
|
|
169
198
|
duration: `${duration}ms`
|
|
170
199
|
});
|
|
171
|
-
return
|
|
200
|
+
return priceData;
|
|
172
201
|
} catch (error) {
|
|
173
202
|
const duration = Date.now() - startTime;
|
|
174
203
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
175
|
-
this.logError(
|
|
204
|
+
this.logError('加载商品价格失败', {
|
|
176
205
|
productCount: ids.length,
|
|
177
206
|
duration: `${duration}ms`,
|
|
178
207
|
error: errorMessage
|
|
@@ -180,6 +209,180 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
180
209
|
return [];
|
|
181
210
|
}
|
|
182
211
|
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* 设置报价单价格桥接器。
|
|
215
|
+
* Server 层负责传入完整 schedule 模块,Products 只负责把报价单计算结果转成价格响应形状。
|
|
216
|
+
*/
|
|
217
|
+
async setupQuotationPriceBridge(params) {
|
|
218
|
+
if (!this.core) {
|
|
219
|
+
this.logWarning('setupQuotationPriceBridge: core 尚未初始化');
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
const channel = params.channel || this.otherParams?.channel;
|
|
223
|
+
this.quotationBridgeChannel = channel;
|
|
224
|
+
if (!this.quotationPriceBridge) {
|
|
225
|
+
const quotation = new _Quotation.QuotationModule(`${this.name}_quotation_price_bridge`);
|
|
226
|
+
await quotation.initialize(this.core, {});
|
|
227
|
+
this.quotationPriceBridge = quotation;
|
|
228
|
+
}
|
|
229
|
+
if (params.scheduleModule) {
|
|
230
|
+
if (this.quotationScheduleCacheSource !== params.scheduleModule) {
|
|
231
|
+
this.quotationScheduleCache.clear();
|
|
232
|
+
this.quotationScheduleCacheLoaded = false;
|
|
233
|
+
this.quotationScheduleCacheSource = params.scheduleModule;
|
|
234
|
+
}
|
|
235
|
+
this.quotationPriceBridge.setScheduleResolver(id => {
|
|
236
|
+
const scheduleId = String(id);
|
|
237
|
+
if (this.quotationScheduleCache.has(scheduleId)) {
|
|
238
|
+
return this.quotationScheduleCache.get(scheduleId);
|
|
239
|
+
}
|
|
240
|
+
if (!this.quotationScheduleCacheLoaded) {
|
|
241
|
+
const scheduleList = params.scheduleModule.getScheduleList?.() || [];
|
|
242
|
+
for (const schedule of scheduleList) {
|
|
243
|
+
this.quotationScheduleCache.set(String(schedule.id), schedule);
|
|
244
|
+
}
|
|
245
|
+
this.quotationScheduleCacheLoaded = true;
|
|
246
|
+
}
|
|
247
|
+
if (this.quotationScheduleCache.has(scheduleId)) {
|
|
248
|
+
return this.quotationScheduleCache.get(scheduleId);
|
|
249
|
+
}
|
|
250
|
+
const schedules = params.scheduleModule.getScheduleListByIds?.([id]) || params.scheduleModule.getScheduleByIds?.([id]) || [];
|
|
251
|
+
if (schedules[0]) this.quotationScheduleCache.set(scheduleId, schedules[0]);
|
|
252
|
+
return schedules[0];
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
await this.refreshQuotationPriceBridge({
|
|
256
|
+
customer_id: params.customer_id
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* 刷新桥接器内的报价单列表。
|
|
262
|
+
*/
|
|
263
|
+
async refreshQuotationPriceBridge(params) {
|
|
264
|
+
if (!this.quotationPriceBridge) return;
|
|
265
|
+
const refreshKey = this.getQuotationBridgeScopeKey();
|
|
266
|
+
if (this.quotationBridgeRefreshPromise && this.quotationBridgeRefreshKey === refreshKey) {
|
|
267
|
+
await this.quotationBridgeRefreshPromise;
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
if (this.quotationBridgeRefreshPromise) {
|
|
271
|
+
await this.quotationBridgeRefreshPromise;
|
|
272
|
+
}
|
|
273
|
+
this.quotationBridgeRefreshKey = refreshKey;
|
|
274
|
+
this.quotationBridgeRefreshPromise = this.quotationPriceBridge.loadQuotations({
|
|
275
|
+
channel: this.quotationBridgeChannel
|
|
276
|
+
}).then(() => {
|
|
277
|
+
this.quotationBridgeLoadedKey = refreshKey;
|
|
278
|
+
}).finally(() => {
|
|
279
|
+
this.quotationBridgeRefreshPromise = undefined;
|
|
280
|
+
this.quotationBridgeRefreshKey = undefined;
|
|
281
|
+
});
|
|
282
|
+
await this.quotationBridgeRefreshPromise;
|
|
283
|
+
}
|
|
284
|
+
async ensureQuotationBridgeReady(customerId) {
|
|
285
|
+
if (!this.quotationPriceBridge) {
|
|
286
|
+
await this.setupQuotationPriceBridge({
|
|
287
|
+
channel: this.otherParams?.channel,
|
|
288
|
+
customer_id: customerId
|
|
289
|
+
});
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
if (this.quotationBridgeRefreshPromise) {
|
|
293
|
+
await this.quotationBridgeRefreshPromise;
|
|
294
|
+
}
|
|
295
|
+
if (this.getQuotationBridgeScopeKey() !== this.quotationBridgeLoadedKey) {
|
|
296
|
+
await this.refreshQuotationPriceBridge({
|
|
297
|
+
customer_id: customerId
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
getQuotationBridgeScopeKey() {
|
|
302
|
+
return this.quotationBridgeChannel || '';
|
|
303
|
+
}
|
|
304
|
+
getQuotedPrice(params) {
|
|
305
|
+
const quotation = this.quotationPriceBridge;
|
|
306
|
+
if (!quotation) return null;
|
|
307
|
+
const query = {
|
|
308
|
+
productId: params.productId,
|
|
309
|
+
variantId: params.variantId,
|
|
310
|
+
datetime: params.datetime,
|
|
311
|
+
customer_id: params.customerId
|
|
312
|
+
};
|
|
313
|
+
if (typeof quotation.getQuotationMatchForProduct === 'function') {
|
|
314
|
+
return quotation.getQuotationMatchForProduct(query)?.price ?? null;
|
|
315
|
+
}
|
|
316
|
+
if (typeof quotation.getPriceForProduct === 'function') {
|
|
317
|
+
return quotation.getPriceForProduct(query);
|
|
318
|
+
}
|
|
319
|
+
return null;
|
|
320
|
+
}
|
|
321
|
+
buildPriceDataFromQuotation(product, datetime, customerId) {
|
|
322
|
+
const quotedPrice = this.getQuotedPrice({
|
|
323
|
+
productId: product.id,
|
|
324
|
+
datetime,
|
|
325
|
+
customerId
|
|
326
|
+
});
|
|
327
|
+
const productPrice = quotedPrice ?? product.price;
|
|
328
|
+
const productBasePrice = quotedPrice ?? product.base_price ?? product.price ?? 0;
|
|
329
|
+
return {
|
|
330
|
+
id: product.id,
|
|
331
|
+
price: String(productPrice ?? 0),
|
|
332
|
+
base_price: String(productBasePrice),
|
|
333
|
+
variant: this.buildVariantPriceData(product, datetime, customerId),
|
|
334
|
+
bundle_group: this.buildBundleGroupPriceData(product, datetime, customerId)
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
buildVariantPriceData(product, datetime, customerId) {
|
|
338
|
+
if (!Array.isArray(product.variant)) return [];
|
|
339
|
+
return product.variant.map(variant => {
|
|
340
|
+
const quotedPrice = this.getQuotedPrice({
|
|
341
|
+
productId: product.id,
|
|
342
|
+
variantId: Number(variant.id),
|
|
343
|
+
datetime,
|
|
344
|
+
customerId
|
|
345
|
+
});
|
|
346
|
+
const price = quotedPrice ?? variant.price ?? product.price ?? 0;
|
|
347
|
+
const basePrice = quotedPrice ?? variant.base_price ?? variant.price ?? product.base_price ?? product.price ?? 0;
|
|
348
|
+
return {
|
|
349
|
+
id: Number(variant.id),
|
|
350
|
+
product_id: Number(variant.product_id ?? product.id),
|
|
351
|
+
base_price: Number(basePrice),
|
|
352
|
+
price: Number(price)
|
|
353
|
+
};
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
buildBundleGroupPriceData(product, datetime, customerId) {
|
|
357
|
+
if (!Array.isArray(product.bundle_group)) return [];
|
|
358
|
+
return product.bundle_group.map(group => ({
|
|
359
|
+
id: Number(group.id),
|
|
360
|
+
bundle_item: (group.bundle_item || []).map(item => {
|
|
361
|
+
const bundleProductId = Number(item.bundle_product_id);
|
|
362
|
+
const bundleVariantId = Number(item.bundle_variant_id || 0);
|
|
363
|
+
const quotedPrice = this.getQuotedPrice({
|
|
364
|
+
productId: bundleProductId,
|
|
365
|
+
variantId: bundleVariantId,
|
|
366
|
+
datetime,
|
|
367
|
+
customerId
|
|
368
|
+
});
|
|
369
|
+
const price = quotedPrice ?? item.price ?? 0;
|
|
370
|
+
const basePrice = quotedPrice ?? item.base_price ?? item.price ?? 0;
|
|
371
|
+
return {
|
|
372
|
+
id: Number(item.id),
|
|
373
|
+
product_id: Number(item.product_id ?? product.id),
|
|
374
|
+
group_id: Number(item.group_id ?? group.id),
|
|
375
|
+
bundle_product_id: bundleProductId,
|
|
376
|
+
bundle_variant_id: bundleVariantId,
|
|
377
|
+
price: Number(price),
|
|
378
|
+
base_price: basePrice,
|
|
379
|
+
price_type: item.price_type,
|
|
380
|
+
price_type_ext: item.price_type_ext
|
|
381
|
+
};
|
|
382
|
+
})
|
|
383
|
+
}));
|
|
384
|
+
}
|
|
385
|
+
|
|
183
386
|
/**
|
|
184
387
|
* 获取应用了价格的商品列表(带缓存)
|
|
185
388
|
* 使用日期作为缓存 key,同一天的商品价格会被缓存
|
|
@@ -188,28 +391,32 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
188
391
|
* @param extraContext 额外的上下文数据(可选,由 Server 层传入)
|
|
189
392
|
* @param options 可选参数
|
|
190
393
|
* @param options.changedIds 变更的商品 IDs,非空时仅对这些商品增量执行 prepare 并更新缓存
|
|
394
|
+
* @param options.productIds 指定商品 IDs,仅对这些商品执行价格计算;不传则处理全量
|
|
191
395
|
* @returns 应用了价格的商品列表
|
|
192
396
|
*/
|
|
193
397
|
async getProductsWithPrice(schedule_date, extraContext, options) {
|
|
194
398
|
const t0 = performance.now();
|
|
195
|
-
const
|
|
196
|
-
const
|
|
399
|
+
const scopedProductIds = this.normalizeProductIds(options?.productIds);
|
|
400
|
+
const cacheKey = this.getProductsPriceCacheKey(schedule_date, extraContext, scopedProductIds);
|
|
401
|
+
const scopedProductIdSet = scopedProductIds ? new Set(scopedProductIds) : undefined;
|
|
402
|
+
const changedIds = scopedProductIdSet ? options?.changedIds?.filter(id => scopedProductIdSet.has(id)) : options?.changedIds;
|
|
197
403
|
if (this.productsPriceCache.has(cacheKey)) {
|
|
198
404
|
const cachedProducts = this.productsPriceCache.get(cacheKey);
|
|
405
|
+
|
|
406
|
+
// 有变更的商品 → 增量 prepare 后 merge 回缓存
|
|
199
407
|
if (changedIds && changedIds.length > 0) {
|
|
200
|
-
this.logInfo(
|
|
408
|
+
this.logInfo('商品价格缓存命中,增量更新变更商品', {
|
|
201
409
|
cacheKey,
|
|
202
410
|
changedIds,
|
|
203
411
|
cachedProductCount: cachedProducts.length
|
|
204
412
|
});
|
|
205
413
|
try {
|
|
206
|
-
const updatedProducts = await this.prepareProductsWithPrice(
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
{ productIds: changedIds }
|
|
210
|
-
);
|
|
414
|
+
const updatedProducts = await this.prepareProductsWithPrice(schedule_date, extraContext, {
|
|
415
|
+
productIds: changedIds
|
|
416
|
+
});
|
|
211
417
|
if (updatedProducts.length > 0) {
|
|
212
|
-
|
|
418
|
+
// merge:用更新结果替换缓存中对应的商品,新增的追加
|
|
419
|
+
const updatedMap = new Map(updatedProducts.map(p => [p.id, p]));
|
|
213
420
|
const mergedCache = [];
|
|
214
421
|
for (const p of cachedProducts) {
|
|
215
422
|
if (updatedMap.has(p.id)) {
|
|
@@ -219,16 +426,17 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
219
426
|
mergedCache.push(p);
|
|
220
427
|
}
|
|
221
428
|
}
|
|
429
|
+
// 剩余的是新增商品(create 场景)
|
|
222
430
|
for (const p of updatedMap.values()) {
|
|
223
431
|
mergedCache.push(p);
|
|
224
432
|
}
|
|
225
433
|
this.productsPriceCache.set(cacheKey, mergedCache);
|
|
226
|
-
this.logInfo(
|
|
434
|
+
this.logInfo('增量更新完成', {
|
|
227
435
|
cacheKey,
|
|
228
436
|
changedCount: updatedProducts.length,
|
|
229
437
|
totalCount: mergedCache.length
|
|
230
438
|
});
|
|
231
|
-
(0,
|
|
439
|
+
(0, _product.perfMark)('getProductsWithPrice(incrementalUpdate)', performance.now() - t0, {
|
|
232
440
|
cacheKey,
|
|
233
441
|
changedCount: changedIds.length,
|
|
234
442
|
count: mergedCache.length
|
|
@@ -237,43 +445,49 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
237
445
|
}
|
|
238
446
|
} catch (error) {
|
|
239
447
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
240
|
-
this.logError(
|
|
448
|
+
this.logError('增量更新失败,返回现有缓存', {
|
|
241
449
|
cacheKey,
|
|
242
450
|
changedIds,
|
|
243
451
|
error: errorMessage
|
|
244
452
|
});
|
|
245
453
|
}
|
|
246
|
-
(0,
|
|
454
|
+
(0, _product.perfMark)('getProductsWithPrice(incrementalUpdate)', performance.now() - t0, {
|
|
247
455
|
cacheKey,
|
|
248
456
|
changedCount: changedIds.length,
|
|
249
457
|
count: cachedProducts.length
|
|
250
458
|
});
|
|
251
459
|
return cachedProducts;
|
|
252
460
|
}
|
|
253
|
-
(0,
|
|
461
|
+
(0, _product.perfMark)('getProductsWithPrice(cacheHit)', performance.now() - t0, {
|
|
254
462
|
cacheKey,
|
|
255
463
|
count: cachedProducts.length
|
|
256
464
|
});
|
|
257
|
-
this.logInfo(
|
|
465
|
+
this.logInfo('商品价格缓存命中', {
|
|
258
466
|
cacheKey,
|
|
259
467
|
productCount: cachedProducts.length
|
|
260
468
|
});
|
|
261
469
|
return cachedProducts;
|
|
262
470
|
}
|
|
263
|
-
this.logInfo(
|
|
264
|
-
|
|
471
|
+
this.logInfo('商品价格缓存未命中,准备获取', {
|
|
472
|
+
cacheKey,
|
|
473
|
+
scopedProductCount: scopedProductIds?.length
|
|
474
|
+
});
|
|
475
|
+
const result = await this.prepareProductsWithPrice(schedule_date, extraContext, scopedProductIds ? {
|
|
476
|
+
productIds: scopedProductIds
|
|
477
|
+
} : undefined);
|
|
265
478
|
this.productsPriceCache.set(cacheKey, result);
|
|
266
|
-
this.logInfo(
|
|
479
|
+
this.logInfo('商品价格已缓存', {
|
|
267
480
|
cacheKey,
|
|
268
481
|
productCount: result.length
|
|
269
482
|
});
|
|
270
483
|
this.cleanExpiredPriceCache();
|
|
271
|
-
(0,
|
|
484
|
+
(0, _product.perfMark)('getProductsWithPrice(cacheMiss)', performance.now() - t0, {
|
|
272
485
|
cacheKey,
|
|
273
486
|
count: result.length
|
|
274
487
|
});
|
|
275
488
|
return result;
|
|
276
489
|
}
|
|
490
|
+
|
|
277
491
|
/**
|
|
278
492
|
* 准备带价格的商品数据(通过格式化器流程处理)
|
|
279
493
|
* @param schedule_date 日期
|
|
@@ -284,22 +498,21 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
284
498
|
* @private
|
|
285
499
|
*/
|
|
286
500
|
async prepareProductsWithPrice(schedule_date, extraContext, options) {
|
|
287
|
-
var _a, _b;
|
|
288
501
|
const tTotal = performance.now();
|
|
289
|
-
const targetIds = options
|
|
290
|
-
const isIncremental = targetIds
|
|
291
|
-
this.logInfo(
|
|
502
|
+
const targetIds = options?.productIds;
|
|
503
|
+
const isIncremental = Array.isArray(targetIds);
|
|
504
|
+
this.logInfo('prepareProductsWithPrice 开始处理', {
|
|
292
505
|
schedule_date,
|
|
293
|
-
mode: isIncremental ?
|
|
294
|
-
targetIdsCount: targetIds
|
|
506
|
+
mode: isIncremental ? 'incremental' : 'full',
|
|
507
|
+
targetIdsCount: targetIds?.length
|
|
295
508
|
});
|
|
296
509
|
try {
|
|
510
|
+
// 1. 获取商品列表:指定 IDs 时从 store 取子集,否则取全量
|
|
297
511
|
let products;
|
|
298
512
|
let ids;
|
|
299
513
|
if (isIncremental) {
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
ids = products.map((p) => p.id);
|
|
514
|
+
products = this.getProductsRefByIds(targetIds);
|
|
515
|
+
ids = products.map(p => p.id);
|
|
303
516
|
} else {
|
|
304
517
|
products = this.getProductsRef();
|
|
305
518
|
const tIds = performance.now();
|
|
@@ -307,56 +520,123 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
307
520
|
for (let i = 0; i < products.length; i++) {
|
|
308
521
|
ids[i] = products[i].id;
|
|
309
522
|
}
|
|
310
|
-
(0,
|
|
523
|
+
(0, _product.perfMark)('prepareProducts.extractIds', performance.now() - tIds, {
|
|
524
|
+
count: ids.length
|
|
525
|
+
});
|
|
311
526
|
}
|
|
312
|
-
this.logInfo(
|
|
313
|
-
|
|
314
|
-
const priceData = await this.loadProductsPrice({
|
|
315
|
-
ids,
|
|
316
|
-
schedule_date
|
|
527
|
+
this.logInfo('获取到商品列表', {
|
|
528
|
+
productCount: products.length
|
|
317
529
|
});
|
|
318
|
-
|
|
319
|
-
|
|
530
|
+
|
|
531
|
+
// 2. 仅在 legacy 报价单 formatter 启用时加载报价单价格;默认由智能定价覆盖商品价格。
|
|
532
|
+
let priceData = [];
|
|
533
|
+
if (this.isLegacyPriceFormatterEnabled) {
|
|
534
|
+
const tPrice = performance.now();
|
|
535
|
+
priceData = await this.loadProductsPrice({
|
|
536
|
+
ids,
|
|
537
|
+
schedule_date,
|
|
538
|
+
schedule_datetime: extraContext?.schedule_datetime,
|
|
539
|
+
customer_id: extraContext?.customer_id
|
|
540
|
+
});
|
|
541
|
+
(0, _product.perfMark)('prepareProducts.loadPrice', performance.now() - tPrice, {
|
|
542
|
+
count: ids.length
|
|
543
|
+
});
|
|
544
|
+
this.logInfo('获取商品报价单价格成功', {
|
|
545
|
+
priceDataCount: priceData?.length ?? 0
|
|
546
|
+
});
|
|
547
|
+
}
|
|
320
548
|
const context = {
|
|
321
549
|
schedule_date,
|
|
550
|
+
schedule_datetime: extraContext?.schedule_datetime,
|
|
551
|
+
customer_id: extraContext?.customer_id,
|
|
322
552
|
priceData,
|
|
323
|
-
locale:
|
|
553
|
+
locale: this.core?.context?.locale,
|
|
554
|
+
dataVariantEvaluator: extraContext?.dataVariantEvaluator || this.core?.context?.dataVariantEvaluator,
|
|
555
|
+
menuList: extraContext?.menuList || [],
|
|
556
|
+
scheduleList: extraContext?.scheduleList || [],
|
|
557
|
+
strategy_context: extraContext?.strategy_context,
|
|
324
558
|
...extraContext
|
|
325
559
|
};
|
|
560
|
+
|
|
561
|
+
// 3. 通过格式化器流程处理商品
|
|
326
562
|
const tFormat = performance.now();
|
|
327
563
|
const processedProducts = await this.applyFormatters(products, context);
|
|
328
|
-
(0,
|
|
564
|
+
(0, _product.perfMark)('prepareProducts.applyFormatters', performance.now() - tFormat, {
|
|
329
565
|
count: products.length,
|
|
330
566
|
formatterCount: this.formatters.length
|
|
331
567
|
});
|
|
332
|
-
this.logInfo(
|
|
333
|
-
mode: isIncremental ?
|
|
568
|
+
this.logInfo('prepareProductsWithPrice 处理完成', {
|
|
569
|
+
mode: isIncremental ? 'incremental' : 'full',
|
|
334
570
|
originalProductCount: products.length,
|
|
335
571
|
processedProductCount: processedProducts.length,
|
|
336
572
|
formatterCount: this.formatters.length
|
|
337
573
|
});
|
|
338
574
|
if (!isIncremental) {
|
|
339
|
-
await this.core.effects.emit(
|
|
340
|
-
import_types.ProductsHooks.onProductsPriceApplied,
|
|
341
|
-
processedProducts
|
|
342
|
-
);
|
|
575
|
+
await this.core.effects.emit(_types.ProductsHooks.onProductsPriceApplied, processedProducts);
|
|
343
576
|
}
|
|
344
|
-
(0,
|
|
577
|
+
(0, _product.perfMark)('prepareProductsWithPrice', performance.now() - tTotal, {
|
|
345
578
|
productCount: products.length,
|
|
346
579
|
formatterCount: this.formatters.length,
|
|
347
|
-
mode: isIncremental ?
|
|
580
|
+
mode: isIncremental ? 'incremental' : 'full'
|
|
348
581
|
});
|
|
349
582
|
return processedProducts;
|
|
350
583
|
} catch (err) {
|
|
351
584
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
352
|
-
|
|
353
|
-
this.logError("prepareProductsWithPrice 处理失败", {
|
|
585
|
+
this.logError('prepareProductsWithPrice 处理失败', {
|
|
354
586
|
schedule_date,
|
|
355
587
|
error: errorMessage
|
|
356
588
|
});
|
|
357
589
|
}
|
|
358
590
|
return [];
|
|
359
591
|
}
|
|
592
|
+
getProductsPriceCacheKey(schedule_date, extraContext, productIds) {
|
|
593
|
+
const datetime = extraContext?.schedule_datetime || schedule_date;
|
|
594
|
+
const productScope = productIds ? `:products:${productIds.join(',')}` : '';
|
|
595
|
+
const strategyScope = this.stringifyStable(extraContext?.strategy_context || {});
|
|
596
|
+
return `${datetime}:customer:${extraContext?.customer_id ?? ''}:strategy:${strategyScope}${productScope}`;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
/**
|
|
600
|
+
* 稳定序列化策略上下文,避免对象 key 顺序不同导致缓存 key 抖动。
|
|
601
|
+
*/
|
|
602
|
+
stringifyStable(value) {
|
|
603
|
+
if (value == null) return '';
|
|
604
|
+
if (Array.isArray(value)) return `[${value.map(item => this.stringifyStable(item)).join(',')}]`;
|
|
605
|
+
if (typeof value === 'object') {
|
|
606
|
+
return `{${Object.keys(value).sort().map(key => `${key}:${this.stringifyStable(value[key])}`).join(',')}}`;
|
|
607
|
+
}
|
|
608
|
+
return String(value);
|
|
609
|
+
}
|
|
610
|
+
normalizeProductIds(productIds) {
|
|
611
|
+
if (!Array.isArray(productIds)) return undefined;
|
|
612
|
+
return Array.from(new Set(productIds.map(id => Number(id)).filter(id => Number.isFinite(id)))).sort((a, b) => a - b);
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
/**
|
|
616
|
+
* 按轻量范围返回商品 ID,不做 structuredClone。
|
|
617
|
+
*/
|
|
618
|
+
getProductIdsByScope(scope) {
|
|
619
|
+
const productIds = this.normalizeProductIds(scope.productIds) || [];
|
|
620
|
+
const collectionIds = this.normalizeProductIds(scope.collectionIds) || [];
|
|
621
|
+
if (scope.isAllProducts) {
|
|
622
|
+
return this.getProductsRef().map(product => product.id);
|
|
623
|
+
}
|
|
624
|
+
if (productIds.length === 0 && collectionIds.length === 0) return [];
|
|
625
|
+
const result = new Set();
|
|
626
|
+
for (const id of productIds) {
|
|
627
|
+
if (this.store.map.has(id)) result.add(id);
|
|
628
|
+
}
|
|
629
|
+
if (collectionIds.length === 0) return Array.from(result);
|
|
630
|
+
const collectionIdSet = new Set(collectionIds);
|
|
631
|
+
for (const product of this.getProductsRef()) {
|
|
632
|
+
if (result.has(product.id)) continue;
|
|
633
|
+
if (!Array.isArray(product.collection)) continue;
|
|
634
|
+
const isInCollection = product.collection.some(collection => collectionIdSet.has(collection.id));
|
|
635
|
+
if (isInCollection) result.add(product.id);
|
|
636
|
+
}
|
|
637
|
+
return Array.from(result);
|
|
638
|
+
}
|
|
639
|
+
|
|
360
640
|
/**
|
|
361
641
|
* 应用所有已注册的格式化器
|
|
362
642
|
* @param products 商品列表
|
|
@@ -367,11 +647,13 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
367
647
|
async applyFormatters(products, context) {
|
|
368
648
|
let result = products;
|
|
369
649
|
if (this.formatters.length === 0) {
|
|
370
|
-
console.warn(
|
|
371
|
-
this.logWarning(
|
|
650
|
+
console.warn('[ProductsModule] ⚠️ 没有注册任何格式化器');
|
|
651
|
+
this.logWarning('没有注册任何格式化器', {
|
|
652
|
+
productCount: products.length
|
|
653
|
+
});
|
|
372
654
|
return result;
|
|
373
655
|
}
|
|
374
|
-
this.logInfo(
|
|
656
|
+
this.logInfo('开始应用格式化器', {
|
|
375
657
|
productCount: products.length,
|
|
376
658
|
formatterCount: this.formatters.length
|
|
377
659
|
});
|
|
@@ -380,7 +662,7 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
380
662
|
try {
|
|
381
663
|
const tF = performance.now();
|
|
382
664
|
result = await formatter(result, context);
|
|
383
|
-
(0,
|
|
665
|
+
(0, _product.perfMark)(`applyFormatters[${i}]`, performance.now() - tF, {
|
|
384
666
|
index: i,
|
|
385
667
|
total: this.formatters.length,
|
|
386
668
|
productCount: result.length
|
|
@@ -395,45 +677,94 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
395
677
|
});
|
|
396
678
|
}
|
|
397
679
|
}
|
|
398
|
-
this.logInfo(
|
|
680
|
+
this.logInfo('所有格式化器已应用', {
|
|
399
681
|
formatterCount: this.formatters.length,
|
|
400
682
|
resultProductCount: result.length
|
|
401
683
|
});
|
|
402
684
|
return result;
|
|
403
685
|
}
|
|
686
|
+
|
|
404
687
|
/**
|
|
405
|
-
*
|
|
406
|
-
*
|
|
688
|
+
* 注册内置商品格式化器。
|
|
689
|
+
*
|
|
690
|
+
* 智能定价默认替代报价单逻辑;报价单 formatter 仅在 enableLegacyPriceFormatter=true 时启用。
|
|
407
691
|
* @private
|
|
408
692
|
*/
|
|
409
|
-
|
|
410
|
-
if (this.
|
|
411
|
-
console.warn(
|
|
693
|
+
registerBuiltinProductFormatters() {
|
|
694
|
+
if (this.isBuiltinFormatterRegistered) {
|
|
695
|
+
console.warn('[ProductsModule] 内置商品格式化器已注册,跳过');
|
|
412
696
|
return;
|
|
413
697
|
}
|
|
414
|
-
|
|
698
|
+
this.isLegacyPriceFormatterEnabled = this.shouldEnableLegacyPriceFormatter();
|
|
699
|
+
const dataVariantFormatter = (products, context) => {
|
|
700
|
+
const evaluator = context.dataVariantEvaluator || this.core?.context?.dataVariantEvaluator;
|
|
701
|
+
if (!evaluator || typeof evaluator.resolveProducts !== 'function') {
|
|
702
|
+
this.lastScheduleTimePoints = [];
|
|
703
|
+
this.logWarning('智能定价评估器未注入,跳过 DataVariant 格式化', {
|
|
704
|
+
productCount: products.length
|
|
705
|
+
});
|
|
706
|
+
return products;
|
|
707
|
+
}
|
|
708
|
+
const strategyContext = context.strategy_context || {};
|
|
709
|
+
const businessData = {
|
|
710
|
+
products,
|
|
711
|
+
scheduleDateTime: context.schedule_datetime || context.schedule_date,
|
|
712
|
+
scheduleList: context.scheduleList || [],
|
|
713
|
+
menuList: context.menuList || [],
|
|
714
|
+
customerId: context.customer_id,
|
|
715
|
+
channel: strategyContext.channel,
|
|
716
|
+
orderType: strategyContext.orderType || strategyContext.order_type,
|
|
717
|
+
businessCode: strategyContext.business_code ?? strategyContext.businessCode,
|
|
718
|
+
availableWalletIds: strategyContext.available_wallet_ids,
|
|
719
|
+
custom: strategyContext
|
|
720
|
+
};
|
|
721
|
+
const result = evaluator.resolveProducts(businessData);
|
|
722
|
+
this.lastScheduleTimePoints = Array.isArray(result?.scheduleTimePoints) ? [...result.scheduleTimePoints] : [];
|
|
723
|
+
return Array.isArray(result?.products) ? result.products : products;
|
|
724
|
+
};
|
|
725
|
+
|
|
726
|
+
// 创建 legacy 报价单格式化器
|
|
727
|
+
const legacyPriceFormatter = (products, context) => {
|
|
415
728
|
if (!context.priceData || context.priceData.length === 0) {
|
|
416
|
-
console.log("[ProductsModule] 💰 没有价格数据,跳过价格应用");
|
|
417
729
|
return products;
|
|
418
730
|
}
|
|
419
|
-
|
|
420
|
-
return (0, import_product.applyPriceDataToProducts)(products, context.priceData);
|
|
731
|
+
return (0, _product.applyPriceDataToProducts)(products, context.priceData);
|
|
421
732
|
};
|
|
422
733
|
const i18nFormatter = (products, context) => {
|
|
423
|
-
return (0,
|
|
734
|
+
return (0, _product.applyI18nToProducts)(products, context.locale);
|
|
424
735
|
};
|
|
425
736
|
const detailValueFormatter = (products, context) => {
|
|
426
|
-
return (0,
|
|
737
|
+
return (0, _product.applyDetailValueToProducts)(products, context);
|
|
427
738
|
};
|
|
428
|
-
this.
|
|
739
|
+
if (this.isLegacyPriceFormatterEnabled) {
|
|
740
|
+
this.formatters.push(legacyPriceFormatter);
|
|
741
|
+
}
|
|
742
|
+
this.formatters.push(dataVariantFormatter);
|
|
429
743
|
this.formatters.push(i18nFormatter);
|
|
430
744
|
this.formatters.push(detailValueFormatter);
|
|
431
|
-
this.
|
|
432
|
-
console.log("[ProductsModule] ✅ 内置价格格式化器已注册(第 1 个)");
|
|
745
|
+
this.isBuiltinFormatterRegistered = true;
|
|
433
746
|
}
|
|
747
|
+
|
|
748
|
+
/**
|
|
749
|
+
* 是否启用旧报价单价格 formatter。
|
|
750
|
+
*
|
|
751
|
+
* 默认关闭,避免报价单逻辑覆盖智能定价结果。
|
|
752
|
+
*/
|
|
753
|
+
shouldEnableLegacyPriceFormatter() {
|
|
754
|
+
return this.otherParams?.enableLegacyPriceFormatter === true || this.core?.context?.enableLegacyPriceFormatter === true;
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
/**
|
|
758
|
+
* 返回最近一次 prepareProductsWithPrice / DataVariant 评估提取的变价时间点(HH:mm)。
|
|
759
|
+
* 供 OS Server 商品 query 订阅定时刷新使用。
|
|
760
|
+
*/
|
|
761
|
+
getLastScheduleTimePoints() {
|
|
762
|
+
return [...this.lastScheduleTimePoints];
|
|
763
|
+
}
|
|
764
|
+
|
|
434
765
|
/**
|
|
435
766
|
* 注册商品格式化器
|
|
436
|
-
*
|
|
767
|
+
* 格式化器会按注册顺序依次执行;legacy 启用时先兜底报价,再由智能定价覆盖。
|
|
437
768
|
* @param formatter 格式化函数
|
|
438
769
|
* @param prepend 是否插入到队列开头(默认 false,追加到末尾)
|
|
439
770
|
* @example
|
|
@@ -448,28 +779,30 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
448
779
|
*/
|
|
449
780
|
registerFormatter(formatter, prepend = false) {
|
|
450
781
|
if (prepend) {
|
|
451
|
-
|
|
782
|
+
// 插入到内置智能定价格式化器之后;legacy 报价单启用时需跳过它。
|
|
783
|
+
const insertIndex = this.isBuiltinFormatterRegistered ? this.isLegacyPriceFormatterEnabled ? 2 : 1 : 0;
|
|
452
784
|
this.formatters.splice(insertIndex, 0, formatter);
|
|
453
|
-
console.log(`[ProductsModule] 📌 已在位置 ${insertIndex + 1} 插入格式化器,当前共 ${this.formatters.length} 个`);
|
|
454
785
|
} else {
|
|
786
|
+
// 追加到末尾
|
|
455
787
|
this.formatters.push(formatter);
|
|
456
|
-
console.log(`[ProductsModule] 📌 已注册格式化器,当前共 ${this.formatters.length} 个`);
|
|
457
788
|
}
|
|
458
789
|
}
|
|
790
|
+
|
|
459
791
|
/**
|
|
460
|
-
*
|
|
461
|
-
* @param keepBuiltin
|
|
792
|
+
* 清空所有格式化器(包括内置智能定价格式化器)
|
|
793
|
+
* @param keepBuiltin 是否保留内置智能定价格式化器(默认 true)
|
|
462
794
|
*/
|
|
463
795
|
clearFormatters(keepBuiltin = true) {
|
|
464
|
-
if (keepBuiltin && this.
|
|
465
|
-
|
|
466
|
-
|
|
796
|
+
if (keepBuiltin && this.isBuiltinFormatterRegistered) {
|
|
797
|
+
// 保留 legacy 兜底(如果启用)和智能定价格式化器。
|
|
798
|
+
const builtinCount = this.isLegacyPriceFormatterEnabled ? 2 : 1;
|
|
799
|
+
this.formatters = this.formatters.slice(0, builtinCount);
|
|
467
800
|
} else {
|
|
468
801
|
this.formatters = [];
|
|
469
|
-
this.
|
|
470
|
-
console.log("[ProductsModule] 🧹 已清空所有格式化器");
|
|
802
|
+
this.isBuiltinFormatterRegistered = false;
|
|
471
803
|
}
|
|
472
804
|
}
|
|
805
|
+
|
|
473
806
|
/**
|
|
474
807
|
* 清理过期的价格缓存
|
|
475
808
|
* 当缓存数量超过限制时,删除最老的缓存
|
|
@@ -480,58 +813,65 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
480
813
|
const keys = Array.from(this.productsPriceCache.keys());
|
|
481
814
|
const oldestKey = keys[0];
|
|
482
815
|
this.productsPriceCache.delete(oldestKey);
|
|
483
|
-
console.log(`[ProductsModule] 🧹 清理过期商品价格缓存: ${oldestKey}`);
|
|
484
816
|
}
|
|
485
817
|
}
|
|
818
|
+
|
|
486
819
|
/**
|
|
487
820
|
* 清空商品价格缓存
|
|
488
821
|
* 可用于手动刷新价格数据
|
|
489
822
|
*/
|
|
490
823
|
clearPriceCache() {
|
|
491
824
|
this.productsPriceCache.clear();
|
|
492
|
-
console.log("[ProductsModule] 🗑️ 商品价格缓存已清空");
|
|
493
825
|
}
|
|
826
|
+
|
|
494
827
|
/**
|
|
495
828
|
* 通过 ProductDataSource SSE 加载完整商品列表
|
|
496
829
|
*/
|
|
497
830
|
async loadProductsByServer() {
|
|
498
831
|
if (!this.productDataSource) {
|
|
499
|
-
this.logWarning(
|
|
832
|
+
this.logWarning('loadProductsByServer: ProductDataSource 不可用');
|
|
500
833
|
return [];
|
|
501
834
|
}
|
|
502
|
-
this.logInfo(
|
|
835
|
+
this.logInfo('开始通过 DataSource SSE 加载商品列表');
|
|
503
836
|
const t0 = performance.now();
|
|
504
837
|
try {
|
|
505
838
|
const tSSE = performance.now();
|
|
506
|
-
const productList = await this.productDataSource.run({
|
|
839
|
+
const productList = await this.productDataSource.run({
|
|
840
|
+
sse: {}
|
|
841
|
+
});
|
|
507
842
|
const list = productList || [];
|
|
508
|
-
(0,
|
|
509
|
-
|
|
843
|
+
(0, _product.perfMark)('loadProductsByServer.SSE', performance.now() - tSSE, {
|
|
844
|
+
count: list.length
|
|
845
|
+
});
|
|
846
|
+
this.logInfo('通过 DataSource SSE 加载商品列表成功', {
|
|
510
847
|
productCount: list.length,
|
|
511
848
|
duration: `${Math.round(performance.now() - t0)}ms`
|
|
512
849
|
});
|
|
513
|
-
await this.
|
|
514
|
-
|
|
515
|
-
|
|
850
|
+
await this.replaceProductsSnapshotInIndexDB(list, 'loadProductsByServer');
|
|
851
|
+
this.lastServerFullFetchAtMs = Date.now();
|
|
852
|
+
await this.core.effects.emit(_types.ProductsHooks.onProductsLoaded, list);
|
|
853
|
+
(0, _product.perfMark)('loadProductsByServer', performance.now() - t0, {
|
|
854
|
+
count: list.length
|
|
855
|
+
});
|
|
516
856
|
return list;
|
|
517
857
|
} catch (error) {
|
|
518
858
|
const duration = Math.round(performance.now() - t0);
|
|
519
859
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
520
|
-
console.error(
|
|
521
|
-
this.logError(
|
|
860
|
+
console.error('[Products] 加载商品数据失败:', error);
|
|
861
|
+
this.logError('通过 DataSource SSE 加载商品列表失败', {
|
|
522
862
|
duration: `${duration}ms`,
|
|
523
863
|
error: errorMessage
|
|
524
864
|
});
|
|
525
865
|
return [];
|
|
526
866
|
}
|
|
527
867
|
}
|
|
868
|
+
|
|
528
869
|
/**
|
|
529
870
|
* 纯请求方法:通过 HTTP 接口获取商品列表(无副作用,不触发事件、不写 IndexDB)
|
|
530
871
|
* @param params 查询参数
|
|
531
872
|
* @returns 商品列表
|
|
532
873
|
*/
|
|
533
874
|
async fetchProductsByHttp(params) {
|
|
534
|
-
var _a, _b;
|
|
535
875
|
const {
|
|
536
876
|
category_ids = [],
|
|
537
877
|
product_ids = [],
|
|
@@ -539,7 +879,7 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
539
879
|
customer_id,
|
|
540
880
|
cacheId
|
|
541
881
|
} = params || {};
|
|
542
|
-
this.logInfo(
|
|
882
|
+
this.logInfo('fetchProductsByHttp: 开始请求', {
|
|
543
883
|
categoryIdsCount: category_ids.length,
|
|
544
884
|
productIdsCount: product_ids.length,
|
|
545
885
|
collectionCount: Array.isArray(collection) ? collection.length : collection ? 1 : 0,
|
|
@@ -547,45 +887,29 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
547
887
|
});
|
|
548
888
|
const startTime = Date.now();
|
|
549
889
|
try {
|
|
550
|
-
const productsData = await this.request.post(
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
open_deposit: 1,
|
|
572
|
-
is_append_product_description: 1,
|
|
573
|
-
with_count: ["bundleGroup", "optionGroup"],
|
|
574
|
-
with_category_tree: 1,
|
|
575
|
-
status: "published",
|
|
576
|
-
num: 500,
|
|
577
|
-
skip: 1,
|
|
578
|
-
category_ids,
|
|
579
|
-
ids: product_ids,
|
|
580
|
-
collection,
|
|
581
|
-
front_end_cache_id: cacheId,
|
|
582
|
-
application_code: (_a = this.otherParams) == null ? void 0 : _a.channel
|
|
583
|
-
},
|
|
584
|
-
{ cache: void 0 }
|
|
585
|
-
);
|
|
586
|
-
const productList = ((_b = productsData == null ? void 0 : productsData.data) == null ? void 0 : _b.list) || [];
|
|
890
|
+
const productsData = await this.request.post(`/product/query`, {
|
|
891
|
+
open_bundle: 1,
|
|
892
|
+
exclude_extension_type: ['product_party', 'product_event', 'product_series_event', 'product_package_ticket', 'ticket', 'event_item'],
|
|
893
|
+
force_ignore_cache_flag: 1,
|
|
894
|
+
with: ['category', 'collection', 'resourceRelation', 'bundleGroup.bundleItem', 'optionGroup.optionItem', 'variantGroup.variantItem', 'dataVariants'],
|
|
895
|
+
open_deposit: 1,
|
|
896
|
+
is_append_product_description: 1,
|
|
897
|
+
with_count: ['bundleGroup', 'optionGroup'],
|
|
898
|
+
with_category_tree: 1,
|
|
899
|
+
status: 'published',
|
|
900
|
+
num: 500,
|
|
901
|
+
skip: 1,
|
|
902
|
+
category_ids,
|
|
903
|
+
ids: product_ids,
|
|
904
|
+
collection,
|
|
905
|
+
front_end_cache_id: cacheId,
|
|
906
|
+
application_code: this.otherParams?.channel
|
|
907
|
+
}, {
|
|
908
|
+
cache: undefined
|
|
909
|
+
});
|
|
910
|
+
const productList = productsData?.data?.list || [];
|
|
587
911
|
const duration = Date.now() - startTime;
|
|
588
|
-
this.logInfo(
|
|
912
|
+
this.logInfo('fetchProductsByHttp: 请求成功', {
|
|
589
913
|
productCount: productList.length,
|
|
590
914
|
duration: `${duration}ms`
|
|
591
915
|
});
|
|
@@ -593,14 +917,15 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
593
917
|
} catch (error) {
|
|
594
918
|
const duration = Date.now() - startTime;
|
|
595
919
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
596
|
-
console.error(
|
|
597
|
-
this.logError(
|
|
920
|
+
console.error('[Products] fetchProductsByHttp 失败:', error);
|
|
921
|
+
this.logError('fetchProductsByHttp: 请求失败', {
|
|
598
922
|
duration: `${duration}ms`,
|
|
599
923
|
error: errorMessage
|
|
600
924
|
});
|
|
601
925
|
return [];
|
|
602
926
|
}
|
|
603
927
|
}
|
|
928
|
+
|
|
604
929
|
/**
|
|
605
930
|
* 加载完整商品列表通过接口(包含所有详细数据)
|
|
606
931
|
* 包含副作用:保存到 IndexDB + 触发 onProductsLoaded 事件
|
|
@@ -609,20 +934,44 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
609
934
|
async loadProductsByServerHttp(params) {
|
|
610
935
|
const productList = await this.fetchProductsByHttp(params);
|
|
611
936
|
if (productList.length > 0) {
|
|
612
|
-
await this.
|
|
613
|
-
|
|
937
|
+
await this.replaceProductsSnapshotInIndexDB(productList, 'loadProductsByServerHttp');
|
|
938
|
+
this.lastServerFullFetchAtMs = Date.now();
|
|
939
|
+
await this.core.effects.emit(_types.ProductsHooks.onProductsLoaded, productList);
|
|
614
940
|
}
|
|
615
941
|
return productList;
|
|
616
942
|
}
|
|
943
|
+
|
|
617
944
|
/**
|
|
618
945
|
* 获取商品列表(深拷贝,供外部安全使用)
|
|
619
946
|
*/
|
|
620
947
|
async getProducts() {
|
|
621
948
|
const t0 = performance.now();
|
|
622
949
|
const result = structuredClone(this.store.list);
|
|
623
|
-
(0,
|
|
950
|
+
(0, _product.perfMark)('ProductsModule.getProducts(structuredClone)', performance.now() - t0, {
|
|
951
|
+
count: result.length
|
|
952
|
+
});
|
|
624
953
|
return result;
|
|
625
954
|
}
|
|
955
|
+
|
|
956
|
+
/**
|
|
957
|
+
* 批量获取商品轻量快照,供订单详情等响应补齐展示协议字段。
|
|
958
|
+
*/
|
|
959
|
+
async getProductSnapshotsByIds(ids) {
|
|
960
|
+
const snapshots = {};
|
|
961
|
+
const seen = new Set();
|
|
962
|
+
for (const id of ids) {
|
|
963
|
+
if (!Number.isFinite(id) || seen.has(id)) continue;
|
|
964
|
+
seen.add(id);
|
|
965
|
+
const product = this.store.map.get(id);
|
|
966
|
+
if (!product) continue;
|
|
967
|
+
snapshots[String(id)] = {
|
|
968
|
+
cover: product.cover,
|
|
969
|
+
holder_config: product.holder_config
|
|
970
|
+
};
|
|
971
|
+
}
|
|
972
|
+
return snapshots;
|
|
973
|
+
}
|
|
974
|
+
|
|
626
975
|
/**
|
|
627
976
|
* 内部获取商品列表的直接引用(无拷贝)
|
|
628
977
|
* 仅供内部 formatter 流程使用,因为 formatter 会创建新对象
|
|
@@ -630,22 +979,39 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
630
979
|
getProductsRef() {
|
|
631
980
|
return this.store.list;
|
|
632
981
|
}
|
|
982
|
+
|
|
983
|
+
/**
|
|
984
|
+
* 按 ID 从 Map 取商品引用,供内部 formatter 流程使用。
|
|
985
|
+
*/
|
|
986
|
+
getProductsRefByIds(ids) {
|
|
987
|
+
const products = [];
|
|
988
|
+
for (const id of ids) {
|
|
989
|
+
const product = this.store.map.get(id);
|
|
990
|
+
if (product) products.push(product);
|
|
991
|
+
}
|
|
992
|
+
return products;
|
|
993
|
+
}
|
|
994
|
+
|
|
633
995
|
/**
|
|
634
996
|
* 根据ID获取单个商品(从内存缓存)
|
|
635
997
|
* 使用 Map 快速查询,时间复杂度 O(1)
|
|
636
998
|
*/
|
|
637
999
|
async getProductById(id) {
|
|
638
1000
|
const product = this.store.map.get(id);
|
|
639
|
-
return product ? structuredClone(product) :
|
|
1001
|
+
return product ? structuredClone(product) : undefined;
|
|
640
1002
|
}
|
|
1003
|
+
|
|
641
1004
|
/**
|
|
642
1005
|
* 根据 ID 列表删除商品(用于 pubsub 同步删除场景)
|
|
643
1006
|
* 同时更新 store.list、store.map、IndexDB 和价格缓存
|
|
644
1007
|
*/
|
|
645
1008
|
async removeProductsByIds(ids) {
|
|
646
1009
|
const idSet = new Set(ids);
|
|
647
|
-
this.logInfo(
|
|
648
|
-
|
|
1010
|
+
this.logInfo('removeProductsByIds', {
|
|
1011
|
+
ids,
|
|
1012
|
+
count: ids.length
|
|
1013
|
+
});
|
|
1014
|
+
this.store.list = this.store.list.filter(p => !idSet.has(p.id));
|
|
649
1015
|
for (const id of ids) {
|
|
650
1016
|
this.store.map.delete(id);
|
|
651
1017
|
}
|
|
@@ -656,113 +1022,202 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
656
1022
|
}
|
|
657
1023
|
} catch (error) {
|
|
658
1024
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
659
|
-
this.logError(
|
|
1025
|
+
this.logError('removeProductsByIds: IndexDB 删除失败', {
|
|
1026
|
+
ids,
|
|
1027
|
+
error: errorMessage
|
|
1028
|
+
});
|
|
660
1029
|
}
|
|
661
1030
|
}
|
|
662
1031
|
for (const [dateKey, cachedProducts] of this.productsPriceCache.entries()) {
|
|
663
|
-
this.productsPriceCache.set(
|
|
664
|
-
dateKey,
|
|
665
|
-
cachedProducts.filter((p) => !idSet.has(p.id))
|
|
666
|
-
);
|
|
1032
|
+
this.productsPriceCache.set(dateKey, cachedProducts.filter(p => !idSet.has(p.id)));
|
|
667
1033
|
}
|
|
668
|
-
this.core.effects.emit(
|
|
669
|
-
this.logInfo(
|
|
1034
|
+
this.core.effects.emit(_types.ProductsHooks.onProductsChanged, this.store.list);
|
|
1035
|
+
this.logInfo('removeProductsByIds 完成', {
|
|
1036
|
+
remaining: this.store.list.length
|
|
1037
|
+
});
|
|
670
1038
|
}
|
|
1039
|
+
|
|
671
1040
|
/**
|
|
672
1041
|
* 重新从服务器加载全量商品列表并更新本地 store
|
|
673
1042
|
* 用于 pubsub 同步 create / update / batch_update 场景
|
|
674
1043
|
*/
|
|
675
1044
|
async refreshProducts() {
|
|
676
1045
|
const tTotal = performance.now();
|
|
677
|
-
this.logInfo(
|
|
1046
|
+
this.logInfo('refreshProducts 开始');
|
|
678
1047
|
const products = await this.loadProductsByServer();
|
|
679
1048
|
if (products && products.length > 0) {
|
|
1049
|
+
// 服务器返回的数据已经是全新的,无需 cloneDeep
|
|
680
1050
|
this.store.list = products;
|
|
681
1051
|
this.syncProductsMap();
|
|
682
|
-
this.core.effects.emit(
|
|
683
|
-
this.logInfo(
|
|
1052
|
+
this.core.effects.emit(_types.ProductsHooks.onProductsChanged, this.store.list);
|
|
1053
|
+
this.logInfo('refreshProducts 完成', {
|
|
1054
|
+
productCount: products.length
|
|
1055
|
+
});
|
|
684
1056
|
} else {
|
|
685
|
-
this.logWarning(
|
|
1057
|
+
this.logWarning('refreshProducts: 服务器未返回数据');
|
|
686
1058
|
}
|
|
687
|
-
(0,
|
|
1059
|
+
(0, _product.perfMark)('refreshProducts', performance.now() - tTotal, {
|
|
1060
|
+
count: this.store.list.length
|
|
1061
|
+
});
|
|
688
1062
|
return this.store.list;
|
|
689
1063
|
}
|
|
1064
|
+
|
|
690
1065
|
/**
|
|
691
1066
|
* 清空缓存
|
|
692
1067
|
*/
|
|
693
1068
|
async clear() {
|
|
694
|
-
this.logInfo(
|
|
1069
|
+
this.logInfo('开始清空缓存', {
|
|
695
1070
|
currentProductCount: this.store.list.length,
|
|
696
1071
|
priceCacheCount: this.productsPriceCache.size
|
|
697
1072
|
});
|
|
698
1073
|
this.store.list = [];
|
|
699
1074
|
this.store.map.clear();
|
|
1075
|
+
|
|
1076
|
+
// 同时清空 IndexDB 中的所有数据(包括商品和元数据)
|
|
700
1077
|
if (this.dbManager) {
|
|
701
1078
|
try {
|
|
702
1079
|
await this.dbManager.clear(INDEXDB_STORE_NAME);
|
|
703
|
-
|
|
704
|
-
this.logInfo("IndexDB 缓存已清空");
|
|
1080
|
+
this.logInfo('IndexDB 缓存已清空');
|
|
705
1081
|
} catch (error) {
|
|
706
1082
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
707
|
-
console.error(
|
|
708
|
-
this.logError(
|
|
1083
|
+
console.error('[Products] 清空 IndexDB 缓存失败:', error);
|
|
1084
|
+
this.logError('清空 IndexDB 缓存失败', {
|
|
1085
|
+
error: errorMessage
|
|
1086
|
+
});
|
|
709
1087
|
}
|
|
710
1088
|
}
|
|
711
|
-
|
|
712
|
-
this.logInfo("缓存清空完成");
|
|
1089
|
+
this.logInfo('缓存清空完成');
|
|
713
1090
|
}
|
|
1091
|
+
|
|
714
1092
|
/**
|
|
715
1093
|
* 从 IndexDB 加载商品数据
|
|
716
1094
|
* @private
|
|
717
1095
|
*/
|
|
718
1096
|
async loadProductsFromIndexDB() {
|
|
719
1097
|
if (!this.dbManager) {
|
|
720
|
-
this.logWarning(
|
|
1098
|
+
this.logWarning('loadProductsFromIndexDB: dbManager 不可用');
|
|
721
1099
|
return [];
|
|
722
1100
|
}
|
|
723
1101
|
try {
|
|
724
1102
|
const t0 = performance.now();
|
|
725
1103
|
const products = await this.dbManager.getAll(INDEXDB_STORE_NAME);
|
|
726
|
-
(0,
|
|
727
|
-
|
|
728
|
-
|
|
1104
|
+
(0, _product.perfMark)('loadProductsFromIndexDB', performance.now() - t0, {
|
|
1105
|
+
count: products?.length ?? 0
|
|
1106
|
+
});
|
|
1107
|
+
this.logInfo('从 IndexDB 加载商品数据', {
|
|
1108
|
+
productCount: products?.length ?? 0
|
|
729
1109
|
});
|
|
730
1110
|
return products || [];
|
|
731
1111
|
} catch (error) {
|
|
732
1112
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
733
|
-
console.error(
|
|
734
|
-
this.logError(
|
|
1113
|
+
console.error('[Products] 从 IndexDB 读取数据失败:', error);
|
|
1114
|
+
this.logError('从 IndexDB 读取数据失败', {
|
|
1115
|
+
error: errorMessage
|
|
1116
|
+
});
|
|
735
1117
|
return [];
|
|
736
1118
|
}
|
|
737
1119
|
}
|
|
1120
|
+
|
|
738
1121
|
/**
|
|
739
|
-
*
|
|
740
|
-
*
|
|
1122
|
+
* 串行执行商品 IndexDB 写入,避免快照替换与增量 patch 并发交错。
|
|
1123
|
+
*
|
|
1124
|
+
* @example
|
|
1125
|
+
* await this.runInProductIndexDBSaveQueue(async () => {
|
|
1126
|
+
* await this.dbManager.bulkUpdate(INDEXDB_STORE_NAME, products)
|
|
1127
|
+
* })
|
|
1128
|
+
*/
|
|
1129
|
+
async runInProductIndexDBSaveQueue(task) {
|
|
1130
|
+
const queuedTask = this.productIndexDBSaveQueue.then(task, task);
|
|
1131
|
+
this.productIndexDBSaveQueue = queuedTask.then(() => undefined, () => undefined);
|
|
1132
|
+
return queuedTask;
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
/**
|
|
1136
|
+
* 全量快照写入 IndexDB:clear + bulkUpdate,仅用于 SSE/HTTP 全量拉取。
|
|
1137
|
+
*
|
|
1138
|
+
* @example
|
|
1139
|
+
* await this.replaceProductsSnapshotInIndexDB(list, 'loadProductsByServer')
|
|
741
1140
|
*/
|
|
742
|
-
async
|
|
1141
|
+
async replaceProductsSnapshotInIndexDB(products, source) {
|
|
743
1142
|
if (!this.dbManager) {
|
|
744
|
-
this.logWarning(
|
|
1143
|
+
this.logWarning('replaceProductsSnapshotInIndexDB: dbManager 不可用');
|
|
745
1144
|
return;
|
|
746
1145
|
}
|
|
747
|
-
this.logInfo("开始保存商品数据到 IndexDB", { productCount: products.length });
|
|
748
1146
|
try {
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
1147
|
+
await this.runInProductIndexDBSaveQueue(async () => {
|
|
1148
|
+
this.logInfo('replaceProductsSnapshotInIndexDB 开始', {
|
|
1149
|
+
source,
|
|
1150
|
+
productCount: products.length
|
|
1151
|
+
});
|
|
1152
|
+
const t0 = performance.now();
|
|
1153
|
+
await this.dbManager.clear(INDEXDB_STORE_NAME);
|
|
1154
|
+
if (products.length > 0) {
|
|
1155
|
+
await this.dbManager.bulkUpdate(INDEXDB_STORE_NAME, products);
|
|
1156
|
+
}
|
|
1157
|
+
(0, _product.perfMark)('replaceProductsSnapshotInIndexDB', performance.now() - t0, {
|
|
1158
|
+
count: products.length
|
|
1159
|
+
});
|
|
1160
|
+
this.logInfo('replaceProductsSnapshotInIndexDB 完成', {
|
|
1161
|
+
source,
|
|
1162
|
+
productCount: products.length
|
|
1163
|
+
});
|
|
1164
|
+
});
|
|
757
1165
|
} catch (error) {
|
|
758
1166
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
759
|
-
|
|
760
|
-
|
|
1167
|
+
this.logError('全量保存商品到 IndexDB 失败', {
|
|
1168
|
+
source,
|
|
761
1169
|
productCount: products.length,
|
|
762
1170
|
error: errorMessage
|
|
763
1171
|
});
|
|
764
1172
|
}
|
|
765
1173
|
}
|
|
1174
|
+
|
|
1175
|
+
/**
|
|
1176
|
+
* 增量 upsert 商品到 IndexDB,不清空表;用于 pubsub 批次合并后的变更商品。
|
|
1177
|
+
*
|
|
1178
|
+
* @example
|
|
1179
|
+
* await this.patchProductsInIndexDB(changedProducts, 'pubsub.processProductSyncMessages', mergeActions)
|
|
1180
|
+
*/
|
|
1181
|
+
async patchProductsInIndexDB(products, source, mergeActions = {}) {
|
|
1182
|
+
if (!this.dbManager || products.length === 0) {
|
|
1183
|
+
return;
|
|
1184
|
+
}
|
|
1185
|
+
const validProducts = products.filter(p => p?.id !== undefined && p?.id !== null);
|
|
1186
|
+
if (validProducts.length === 0) {
|
|
1187
|
+
return;
|
|
1188
|
+
}
|
|
1189
|
+
try {
|
|
1190
|
+
await this.runInProductIndexDBSaveQueue(async () => {
|
|
1191
|
+
this.logInfo('patchProductsInIndexDB 开始', {
|
|
1192
|
+
source,
|
|
1193
|
+
count: validProducts.length
|
|
1194
|
+
});
|
|
1195
|
+
const t0 = performance.now();
|
|
1196
|
+
if (typeof this.dbManager.bulkUpdate === 'function') {
|
|
1197
|
+
await this.dbManager.bulkUpdate(INDEXDB_STORE_NAME, validProducts);
|
|
1198
|
+
} else if (typeof this.dbManager.update === 'function') {
|
|
1199
|
+
for (const product of validProducts) {
|
|
1200
|
+
await this.dbManager.update(INDEXDB_STORE_NAME, product);
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
(0, _product.perfMark)('patchProductsInIndexDB', performance.now() - t0, {
|
|
1204
|
+
count: validProducts.length
|
|
1205
|
+
});
|
|
1206
|
+
this.logInfo('patchProductsInIndexDB 完成', {
|
|
1207
|
+
source,
|
|
1208
|
+
count: validProducts.length
|
|
1209
|
+
});
|
|
1210
|
+
});
|
|
1211
|
+
} catch (error) {
|
|
1212
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1213
|
+
this.logError('增量保存商品到 IndexDB 失败', {
|
|
1214
|
+
source,
|
|
1215
|
+
error: errorMessage,
|
|
1216
|
+
productCount: validProducts.length
|
|
1217
|
+
});
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
|
|
766
1221
|
/**
|
|
767
1222
|
* 同步更新商品 Map 缓存
|
|
768
1223
|
* 将 list 中的商品同步到 map,以 id 为 key
|
|
@@ -774,90 +1229,105 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
774
1229
|
for (const product of this.store.list) {
|
|
775
1230
|
this.store.map.set(product.id, product);
|
|
776
1231
|
}
|
|
777
|
-
(0,
|
|
1232
|
+
(0, _product.perfMark)('syncProductsMap', performance.now() - t0, {
|
|
1233
|
+
count: this.store.map.size
|
|
1234
|
+
});
|
|
778
1235
|
}
|
|
1236
|
+
|
|
779
1237
|
/**
|
|
780
1238
|
* 预加载模块数据(统一接口)
|
|
781
1239
|
* 在模块注册后自动调用
|
|
782
1240
|
*/
|
|
783
1241
|
async preload() {
|
|
784
|
-
console.log("[Products] 开始预加载数据...");
|
|
785
1242
|
const tTotal = performance.now();
|
|
786
|
-
this.logInfo(
|
|
1243
|
+
this.logInfo('开始预加载数据');
|
|
787
1244
|
try {
|
|
788
1245
|
const tIndexDB = performance.now();
|
|
789
1246
|
const cachedData = await this.loadProductsFromIndexDB();
|
|
790
|
-
(0,
|
|
1247
|
+
(0, _product.perfMark)('preload.loadFromIndexDB', performance.now() - tIndexDB, {
|
|
1248
|
+
count: cachedData?.length ?? 0
|
|
1249
|
+
});
|
|
791
1250
|
if (cachedData && cachedData.length > 0) {
|
|
792
|
-
|
|
1251
|
+
// IndexDB 反序列化的数据已经是全新的,无需 cloneDeep
|
|
793
1252
|
this.store.list = cachedData;
|
|
794
1253
|
const tSync = performance.now();
|
|
795
1254
|
this.syncProductsMap();
|
|
796
|
-
(0,
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
)
|
|
801
|
-
(0, import_product.perfMark)("preload(IndexDB)", performance.now() - tTotal, {
|
|
1255
|
+
(0, _product.perfMark)('preload.syncProductsMap', performance.now() - tSync, {
|
|
1256
|
+
count: cachedData.length
|
|
1257
|
+
});
|
|
1258
|
+
this.core.effects.emit(_types.ProductsHooks.onProductsChanged, this.store.list);
|
|
1259
|
+
(0, _product.perfMark)('preload(IndexDB)', performance.now() - tTotal, {
|
|
802
1260
|
count: cachedData.length,
|
|
803
|
-
source:
|
|
1261
|
+
source: 'IndexDB'
|
|
804
1262
|
});
|
|
805
|
-
this.logInfo(
|
|
1263
|
+
this.logInfo('预加载完成(从 IndexDB)', {
|
|
806
1264
|
productCount: cachedData.length,
|
|
807
1265
|
duration: `${Math.round(performance.now() - tTotal)}ms`,
|
|
808
|
-
source:
|
|
1266
|
+
source: 'IndexDB'
|
|
809
1267
|
});
|
|
1268
|
+
this.lastPreloadCompletedAtMs = Date.now();
|
|
810
1269
|
return;
|
|
811
1270
|
}
|
|
812
|
-
|
|
813
|
-
this.logInfo("IndexDB 中没有缓存数据,准备从服务器加载");
|
|
1271
|
+
this.logInfo('IndexDB 中没有缓存数据,准备从服务器加载');
|
|
814
1272
|
} catch (error) {
|
|
815
1273
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
816
|
-
console.warn(
|
|
817
|
-
this.logWarning(
|
|
1274
|
+
console.warn('[Products] 从 IndexDB 加载数据失败:', error);
|
|
1275
|
+
this.logWarning('从 IndexDB 加载数据失败,准备从服务器加载', {
|
|
1276
|
+
error: errorMessage
|
|
1277
|
+
});
|
|
818
1278
|
}
|
|
819
1279
|
const tServer = performance.now();
|
|
820
1280
|
const products = await this.loadProductsByServer();
|
|
821
|
-
(0,
|
|
1281
|
+
(0, _product.perfMark)('preload.loadFromServer', performance.now() - tServer, {
|
|
1282
|
+
count: products?.length ?? 0
|
|
1283
|
+
});
|
|
822
1284
|
if (products && products.length > 0) {
|
|
1285
|
+
// 服务器返回的数据已经是全新的,无需 cloneDeep
|
|
823
1286
|
this.store.list = products;
|
|
824
1287
|
const tSync = performance.now();
|
|
825
1288
|
this.syncProductsMap();
|
|
826
|
-
(0,
|
|
827
|
-
|
|
828
|
-
|
|
1289
|
+
(0, _product.perfMark)('preload.syncProductsMap', performance.now() - tSync, {
|
|
1290
|
+
count: products.length
|
|
1291
|
+
});
|
|
1292
|
+
this.core.effects.emit(_types.ProductsHooks.onProductsChanged, this.store.list);
|
|
1293
|
+
(0, _product.perfMark)('preload(Server)', performance.now() - tTotal, {
|
|
829
1294
|
count: products.length,
|
|
830
|
-
source:
|
|
1295
|
+
source: 'Server'
|
|
831
1296
|
});
|
|
832
|
-
this.logInfo(
|
|
1297
|
+
this.logInfo('预加载完成(从服务器)', {
|
|
833
1298
|
productCount: products.length,
|
|
834
1299
|
duration: `${Math.round(performance.now() - tTotal)}ms`,
|
|
835
|
-
source:
|
|
1300
|
+
source: 'Server'
|
|
836
1301
|
});
|
|
1302
|
+
this.lastPreloadCompletedAtMs = Date.now();
|
|
837
1303
|
} else {
|
|
838
|
-
(0,
|
|
839
|
-
|
|
1304
|
+
(0, _product.perfMark)('preload(empty)', performance.now() - tTotal, {
|
|
1305
|
+
source: 'empty'
|
|
1306
|
+
});
|
|
1307
|
+
this.logWarning('预加载完成但未获取到数据', {
|
|
840
1308
|
duration: `${Math.round(performance.now() - tTotal)}ms`
|
|
841
1309
|
});
|
|
842
1310
|
}
|
|
843
1311
|
}
|
|
1312
|
+
|
|
844
1313
|
// =============================================
|
|
845
1314
|
// 商品数据同步(pubsub)
|
|
846
1315
|
// =============================================
|
|
1316
|
+
|
|
847
1317
|
/**
|
|
848
1318
|
* 初始化 ProductDataSource 实例
|
|
849
1319
|
* 与 pubsub 订阅和数据获取分开,仅负责创建实例
|
|
850
1320
|
*/
|
|
851
1321
|
initProductDataSource() {
|
|
852
|
-
|
|
853
|
-
const ProductDataSourceClass = (_b = (_a = this.core.serverOptions) == null ? void 0 : _a.All_DATA_SOURCES) == null ? void 0 : _b.ProductDataSource;
|
|
1322
|
+
const ProductDataSourceClass = this.core.serverOptions?.All_DATA_SOURCES?.ProductDataSource;
|
|
854
1323
|
if (!ProductDataSourceClass) {
|
|
855
|
-
this.logWarning(
|
|
1324
|
+
this.logWarning('initProductDataSource: ProductDataSource 不可用');
|
|
856
1325
|
return;
|
|
857
1326
|
}
|
|
858
1327
|
this.productDataSource = new ProductDataSourceClass();
|
|
859
|
-
this.logInfo(
|
|
1328
|
+
this.logInfo('ProductDataSource 实例已创建');
|
|
860
1329
|
}
|
|
1330
|
+
|
|
861
1331
|
/**
|
|
862
1332
|
* 初始化 pubsub 订阅,监听管理后台商品变更
|
|
863
1333
|
* 仅负责订阅 product / product_quotation 频道,消息通过防抖合并后批量处理
|
|
@@ -865,21 +1335,22 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
865
1335
|
*/
|
|
866
1336
|
setupProductSync() {
|
|
867
1337
|
if (!this.productDataSource) {
|
|
868
|
-
this.logWarning(
|
|
1338
|
+
this.logWarning('setupProductSync: ProductDataSource 不可用,跳过同步初始化');
|
|
869
1339
|
return;
|
|
870
1340
|
}
|
|
871
|
-
const createHandler =
|
|
872
|
-
const data =
|
|
873
|
-
if (!data)
|
|
874
|
-
|
|
875
|
-
console.log(`[ProductsModule] 收到同步消息 [${channelKey}]:`, data);
|
|
876
|
-
this.logInfo("收到同步消息", {
|
|
1341
|
+
const createHandler = channelKey => message => {
|
|
1342
|
+
const data = message?.data || message;
|
|
1343
|
+
if (!data) return;
|
|
1344
|
+
this.logInfo('收到同步消息', {
|
|
877
1345
|
channelKey,
|
|
878
1346
|
action: data.action,
|
|
879
1347
|
id: data.id,
|
|
880
1348
|
action_filed: data.action_filed
|
|
881
1349
|
});
|
|
882
|
-
this.pendingSyncMessages.push({
|
|
1350
|
+
this.pendingSyncMessages.push({
|
|
1351
|
+
...data,
|
|
1352
|
+
_channelKey: channelKey
|
|
1353
|
+
});
|
|
883
1354
|
if (this.syncTimer) {
|
|
884
1355
|
clearTimeout(this.syncTimer);
|
|
885
1356
|
}
|
|
@@ -889,25 +1360,25 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
889
1360
|
};
|
|
890
1361
|
this.productDataSource.run({
|
|
891
1362
|
pubsub: {
|
|
892
|
-
callback:
|
|
893
|
-
console.log(
|
|
894
|
-
this.logInfo(
|
|
895
|
-
|
|
896
|
-
});
|
|
897
|
-
if (!
|
|
898
|
-
return;
|
|
1363
|
+
callback: res => {
|
|
1364
|
+
// console.log('sse_products_callback', res)
|
|
1365
|
+
// this.logInfo('sse_products_callback: 收到同步消息', {
|
|
1366
|
+
// data: res?.data,
|
|
1367
|
+
// });
|
|
1368
|
+
if (!res?.data) return;
|
|
899
1369
|
const data = res.data;
|
|
900
|
-
const channelKey = data.module ||
|
|
1370
|
+
const channelKey = data.module || 'product';
|
|
901
1371
|
createHandler(channelKey)(data);
|
|
902
1372
|
}
|
|
903
1373
|
}
|
|
904
|
-
}).catch(
|
|
905
|
-
this.logError(
|
|
1374
|
+
}).catch(err => {
|
|
1375
|
+
this.logError('setupProductSync: DataSource run 出错', {
|
|
906
1376
|
error: err instanceof Error ? err.message : String(err)
|
|
907
1377
|
});
|
|
908
1378
|
});
|
|
909
|
-
this.logInfo(
|
|
1379
|
+
this.logInfo('setupProductSync: pubsub 订阅已建立');
|
|
910
1380
|
}
|
|
1381
|
+
|
|
911
1382
|
/**
|
|
912
1383
|
* 处理防抖后的同步消息批次
|
|
913
1384
|
*
|
|
@@ -927,87 +1398,127 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
927
1398
|
* Server 层监听该事件后对变更商品增量执行 prepareProductsWithPrice 并更新缓存
|
|
928
1399
|
*/
|
|
929
1400
|
async processProductSyncMessages() {
|
|
930
|
-
var _a, _b, _c, _d, _e, _f;
|
|
931
1401
|
const messages = [...this.pendingSyncMessages];
|
|
932
1402
|
this.pendingSyncMessages = [];
|
|
933
|
-
if (messages.length === 0)
|
|
934
|
-
|
|
935
|
-
|
|
1403
|
+
if (messages.length === 0) return;
|
|
1404
|
+
this.logInfo('processProductSyncMessages: 开始处理', {
|
|
1405
|
+
count: messages.length
|
|
1406
|
+
});
|
|
936
1407
|
const deleteIds = [];
|
|
937
|
-
const bodyUpdates =
|
|
1408
|
+
const bodyUpdates = new Map();
|
|
938
1409
|
const sseRefreshIds = [];
|
|
939
1410
|
const priceRefreshIds = [];
|
|
940
1411
|
let shouldClearPriceCache = false;
|
|
941
1412
|
for (const msg of messages) {
|
|
942
|
-
const channelKey = msg._channelKey || msg.module ||
|
|
943
|
-
if (channelKey ===
|
|
944
|
-
if (
|
|
1413
|
+
const channelKey = msg._channelKey || msg.module || 'product';
|
|
1414
|
+
if (channelKey === 'product') {
|
|
1415
|
+
if (msg.relation_product_ids?.length) {
|
|
945
1416
|
sseRefreshIds.push(...msg.relation_product_ids);
|
|
946
1417
|
}
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
else if (msg.id)
|
|
951
|
-
deleteIds.push(msg.id);
|
|
1418
|
+
// 1. 删除场景:operation === 'delete' 或 action === 'delete'
|
|
1419
|
+
if (msg.operation === 'delete' || msg.action === 'delete') {
|
|
1420
|
+
if (msg.ids?.length) deleteIds.push(...msg.ids);else if (msg.id) deleteIds.push(msg.id);
|
|
952
1421
|
continue;
|
|
953
1422
|
}
|
|
954
|
-
|
|
1423
|
+
|
|
1424
|
+
// 2. change_types 包含 price → 仅收集需要刷新价格的 IDs(不走 SSE 拉取)
|
|
1425
|
+
if (msg.change_types?.includes('price')) {
|
|
955
1426
|
const ids = msg.ids || (msg.id ? [msg.id] : []);
|
|
956
1427
|
priceRefreshIds.push(...ids);
|
|
957
1428
|
if (msg.body) {
|
|
958
1429
|
const bodyId = msg.body.id;
|
|
959
|
-
if (bodyId)
|
|
960
|
-
bodyUpdates.set(bodyId, msg.body);
|
|
1430
|
+
if (bodyId) bodyUpdates.set(bodyId, msg.body);
|
|
961
1431
|
}
|
|
962
1432
|
continue;
|
|
963
1433
|
}
|
|
1434
|
+
|
|
1435
|
+
// 3. 有 body → 完整数据直接覆盖本地
|
|
964
1436
|
if (msg.body) {
|
|
965
1437
|
const bodyId = msg.body.id || msg.id;
|
|
966
|
-
if (bodyId)
|
|
967
|
-
bodyUpdates.set(bodyId, msg.body);
|
|
1438
|
+
if (bodyId) bodyUpdates.set(bodyId, msg.body);
|
|
968
1439
|
continue;
|
|
969
1440
|
}
|
|
970
|
-
|
|
1441
|
+
|
|
1442
|
+
// 4. 其他情况(有 ids 无 body 无 change_types)→ SSE 拉取
|
|
1443
|
+
if (msg.ids?.length) {
|
|
971
1444
|
sseRefreshIds.push(...msg.ids);
|
|
972
1445
|
} else if (msg.id) {
|
|
973
1446
|
sseRefreshIds.push(msg.id);
|
|
974
1447
|
}
|
|
975
|
-
} else if (channelKey ===
|
|
1448
|
+
} else if (channelKey === 'product_quotation') {
|
|
1449
|
+
// 报价单变更影响范围大,标记清除全量价格缓存
|
|
976
1450
|
shouldClearPriceCache = true;
|
|
977
|
-
if (
|
|
1451
|
+
if (msg.relation_product_ids?.length) {
|
|
978
1452
|
sseRefreshIds.push(...msg.relation_product_ids);
|
|
979
1453
|
}
|
|
980
|
-
} else if ([
|
|
981
|
-
if (
|
|
1454
|
+
} else if (['product_collection', 'product_category'].includes(channelKey)) {
|
|
1455
|
+
if (msg.relation_product_ids?.length) {
|
|
982
1456
|
sseRefreshIds.push(...msg.relation_product_ids);
|
|
983
1457
|
}
|
|
1458
|
+
} else if (['product_data_variant'].includes(channelKey)) {
|
|
1459
|
+
if (msg.product_ids?.length) {
|
|
1460
|
+
sseRefreshIds.push(...msg.product_ids);
|
|
1461
|
+
}
|
|
984
1462
|
}
|
|
985
1463
|
}
|
|
986
1464
|
const uniqueDeleteIds = [...new Set(deleteIds)];
|
|
987
1465
|
const uniqueSSEIds = [...new Set(sseRefreshIds)];
|
|
988
1466
|
const uniquePriceIds = [...new Set(priceRefreshIds)];
|
|
1467
|
+
|
|
1468
|
+
// 1. 处理删除
|
|
989
1469
|
if (uniqueDeleteIds.length > 0) {
|
|
990
1470
|
await this.removeProductsByIds(uniqueDeleteIds);
|
|
991
1471
|
}
|
|
1472
|
+
|
|
1473
|
+
/** 本批次待 patch 的商品(按 id 去重,SSE 覆盖 body) */
|
|
1474
|
+
const patchProductMap = new Map();
|
|
1475
|
+
const patchMergeActions = {};
|
|
1476
|
+
let storeMutated = uniqueDeleteIds.length > 0;
|
|
1477
|
+
|
|
1478
|
+
// 2. 处理 body 直接覆盖(仅内存,IndexDB 批次末统一 patch)
|
|
992
1479
|
if (bodyUpdates.size > 0) {
|
|
993
|
-
await this.applyBodyUpdatesToStore(bodyUpdates
|
|
1480
|
+
const bodyResult = await this.applyBodyUpdatesToStore(bodyUpdates, {
|
|
1481
|
+
skipIndexDB: true,
|
|
1482
|
+
skipEmit: true
|
|
1483
|
+
});
|
|
1484
|
+
for (const product of bodyResult.changedProducts) {
|
|
1485
|
+
patchProductMap.set(product.id, product);
|
|
1486
|
+
patchMergeActions[product.id] = bodyResult.mergeActions[product.id];
|
|
1487
|
+
}
|
|
1488
|
+
storeMutated = true;
|
|
994
1489
|
}
|
|
1490
|
+
|
|
1491
|
+
// 3. 处理 SSE 增量拉取(仅内存,IndexDB 批次末统一 patch)
|
|
1492
|
+
let freshProducts = [];
|
|
995
1493
|
if (uniqueSSEIds.length > 0) {
|
|
996
|
-
|
|
1494
|
+
freshProducts = await this.fetchProductsBySSE(uniqueSSEIds);
|
|
997
1495
|
if (freshProducts.length > 0) {
|
|
998
|
-
await this.mergeProductsToStore(freshProducts
|
|
1496
|
+
const mergeResult = await this.mergeProductsToStore(freshProducts, {
|
|
1497
|
+
skipIndexDB: true,
|
|
1498
|
+
skipEmit: true
|
|
1499
|
+
});
|
|
1500
|
+
for (const product of mergeResult.changedProducts) {
|
|
1501
|
+
patchProductMap.set(product.id, product);
|
|
1502
|
+
patchMergeActions[product.id] = mergeResult.mergeActions[product.id];
|
|
1503
|
+
}
|
|
1504
|
+
storeMutated = true;
|
|
999
1505
|
}
|
|
1000
|
-
this.logInfo(
|
|
1506
|
+
this.logInfo('processProductSyncMessages: SSE 增量更新完成123', {
|
|
1001
1507
|
requestedCount: uniqueSSEIds.length,
|
|
1002
1508
|
receivedCount: freshProducts.length
|
|
1003
1509
|
});
|
|
1004
1510
|
}
|
|
1005
|
-
const
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1511
|
+
const patchProducts = [...patchProductMap.values()];
|
|
1512
|
+
if (patchProducts.length > 0) {
|
|
1513
|
+
await this.patchProductsInIndexDB(patchProducts, 'pubsub.processProductSyncMessages', patchMergeActions);
|
|
1514
|
+
}
|
|
1515
|
+
if (storeMutated) {
|
|
1516
|
+
this.core.effects.emit(_types.ProductsHooks.onProductsChanged, this.store.list);
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1519
|
+
// 收集所有非 delete 的变更 IDs(body + SSE + price),用于增量更新价格缓存
|
|
1520
|
+
const allChangedIds = [...new Set([...Array.from(bodyUpdates.keys()), ...uniqueSSEIds, ...uniquePriceIds])];
|
|
1521
|
+
this.logInfo('processProductSyncMessages: 处理完成123', {
|
|
1011
1522
|
deleteCount: uniqueDeleteIds.length,
|
|
1012
1523
|
bodyUpdateCount: bodyUpdates.size,
|
|
1013
1524
|
sseRefreshCount: uniqueSSEIds.length,
|
|
@@ -1017,34 +1528,55 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
1017
1528
|
});
|
|
1018
1529
|
const hasChanges = uniqueDeleteIds.length > 0 || allChangedIds.length > 0 || shouldClearPriceCache;
|
|
1019
1530
|
if (!hasChanges) {
|
|
1020
|
-
this.logInfo(
|
|
1531
|
+
this.logInfo('processProductSyncMessages: 没有变更,不触发 onProductsSyncCompleted');
|
|
1021
1532
|
return;
|
|
1022
1533
|
}
|
|
1534
|
+
|
|
1535
|
+
// 报价单变更 → 清除全量价格缓存,后续走全量重建
|
|
1023
1536
|
if (shouldClearPriceCache) {
|
|
1537
|
+
try {
|
|
1538
|
+
await this.refreshQuotationPriceBridge();
|
|
1539
|
+
} catch (error) {
|
|
1540
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1541
|
+
this.logError('报价单桥接刷新失败,将继续清理价格缓存', {
|
|
1542
|
+
error: errorMessage
|
|
1543
|
+
});
|
|
1544
|
+
}
|
|
1024
1545
|
this.clearPriceCache();
|
|
1025
1546
|
}
|
|
1026
|
-
await this.core.effects.emit(
|
|
1547
|
+
await this.core.effects.emit(_types.ProductsHooks.onProductsSyncCompleted, {
|
|
1027
1548
|
changedIds: allChangedIds
|
|
1028
1549
|
});
|
|
1029
1550
|
}
|
|
1551
|
+
|
|
1030
1552
|
/**
|
|
1031
1553
|
* 通过 SSE 按 ids 增量拉取商品数据
|
|
1032
1554
|
* 请求 GET /shop/core/stream?type=product&ids={ids}
|
|
1033
1555
|
*/
|
|
1034
1556
|
async fetchProductsBySSE(ids) {
|
|
1035
1557
|
if (!this.productDataSource) {
|
|
1036
|
-
this.logWarning(
|
|
1558
|
+
this.logWarning('fetchProductsBySSE: ProductDataSource 不可用');
|
|
1037
1559
|
return [];
|
|
1038
1560
|
}
|
|
1039
|
-
this.logInfo(
|
|
1561
|
+
this.logInfo('fetchProductsBySSE: 开始', {
|
|
1562
|
+
ids,
|
|
1563
|
+
count: ids.length
|
|
1564
|
+
});
|
|
1040
1565
|
const t0 = performance.now();
|
|
1041
1566
|
try {
|
|
1042
1567
|
const productList = await this.productDataSource.run({
|
|
1043
|
-
sse: {
|
|
1568
|
+
sse: {
|
|
1569
|
+
query: {
|
|
1570
|
+
type: 'product',
|
|
1571
|
+
ids
|
|
1572
|
+
}
|
|
1573
|
+
}
|
|
1044
1574
|
});
|
|
1045
1575
|
const list = productList || [];
|
|
1046
|
-
(0,
|
|
1047
|
-
|
|
1576
|
+
(0, _product.perfMark)('fetchProductsBySSE', performance.now() - t0, {
|
|
1577
|
+
count: list.length
|
|
1578
|
+
});
|
|
1579
|
+
this.logInfo('fetchProductsBySSE: 成功', {
|
|
1048
1580
|
requestedCount: ids.length,
|
|
1049
1581
|
receivedCount: list.length,
|
|
1050
1582
|
duration: `${Math.round(performance.now() - t0)}ms`
|
|
@@ -1052,26 +1584,39 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
1052
1584
|
return list;
|
|
1053
1585
|
} catch (error) {
|
|
1054
1586
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1055
|
-
this.logError(
|
|
1587
|
+
this.logError('fetchProductsBySSE: 失败', {
|
|
1588
|
+
ids,
|
|
1589
|
+
error: errorMessage
|
|
1590
|
+
});
|
|
1056
1591
|
return [];
|
|
1057
1592
|
}
|
|
1058
1593
|
}
|
|
1594
|
+
|
|
1059
1595
|
/**
|
|
1060
1596
|
* 将 body 完整数据直接覆盖到本地 store(不调用报价单接口)
|
|
1061
1597
|
* 已存在的 → 直接替换;不存在的 → 追加
|
|
1062
|
-
*
|
|
1063
|
-
*
|
|
1598
|
+
*
|
|
1599
|
+
* @example
|
|
1600
|
+
* await this.applyBodyUpdatesToStore(bodyUpdates, { skipIndexDB: true })
|
|
1064
1601
|
*/
|
|
1065
|
-
async applyBodyUpdatesToStore(bodyUpdates) {
|
|
1066
|
-
|
|
1602
|
+
async applyBodyUpdatesToStore(bodyUpdates, options) {
|
|
1603
|
+
const changedProductIds = [...bodyUpdates.keys()];
|
|
1604
|
+
this.logInfo('applyBodyUpdatesToStore: 开始', {
|
|
1605
|
+
count: bodyUpdates.size
|
|
1606
|
+
});
|
|
1067
1607
|
let updatedCount = 0;
|
|
1068
1608
|
let newCount = 0;
|
|
1069
|
-
const appliedIds =
|
|
1070
|
-
|
|
1609
|
+
const appliedIds = new Set();
|
|
1610
|
+
const mergeActions = {};
|
|
1611
|
+
const changedProducts = [];
|
|
1612
|
+
this.store.list = this.store.list.map(p => {
|
|
1071
1613
|
if (bodyUpdates.has(p.id)) {
|
|
1072
1614
|
updatedCount++;
|
|
1073
1615
|
appliedIds.add(p.id);
|
|
1074
|
-
|
|
1616
|
+
mergeActions[p.id] = 'update';
|
|
1617
|
+
const next = bodyUpdates.get(p.id);
|
|
1618
|
+
changedProducts.push(next);
|
|
1619
|
+
return next;
|
|
1075
1620
|
}
|
|
1076
1621
|
return p;
|
|
1077
1622
|
});
|
|
@@ -1079,50 +1624,85 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
1079
1624
|
if (!appliedIds.has(id)) {
|
|
1080
1625
|
this.store.list.push(body);
|
|
1081
1626
|
newCount++;
|
|
1627
|
+
mergeActions[id] = 'insert';
|
|
1628
|
+
changedProducts.push(body);
|
|
1082
1629
|
}
|
|
1083
1630
|
}
|
|
1084
1631
|
this.syncProductsMap();
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1632
|
+
if (!options?.skipIndexDB) {
|
|
1633
|
+
await this.patchProductsInIndexDB(changedProducts, 'pubsub.bodyUpdate', mergeActions);
|
|
1634
|
+
}
|
|
1635
|
+
if (!options?.skipEmit) {
|
|
1636
|
+
this.core.effects.emit(_types.ProductsHooks.onProductsChanged, this.store.list);
|
|
1637
|
+
}
|
|
1638
|
+
this.logInfo('applyBodyUpdatesToStore: 完成', {
|
|
1088
1639
|
updatedCount,
|
|
1089
1640
|
newCount,
|
|
1090
1641
|
totalCount: this.store.list.length
|
|
1091
1642
|
});
|
|
1643
|
+
return {
|
|
1644
|
+
changedProducts,
|
|
1645
|
+
mergeActions,
|
|
1646
|
+
updatedCount,
|
|
1647
|
+
newCount
|
|
1648
|
+
};
|
|
1092
1649
|
}
|
|
1650
|
+
|
|
1093
1651
|
/**
|
|
1094
1652
|
* 将增量拉取的商品合并到 store
|
|
1095
1653
|
* 已存在的 → 替换;新的 → 追加
|
|
1096
|
-
*
|
|
1654
|
+
*
|
|
1655
|
+
* @example
|
|
1656
|
+
* await this.mergeProductsToStore(freshProducts, { skipIndexDB: true })
|
|
1097
1657
|
*/
|
|
1098
|
-
async mergeProductsToStore(freshProducts) {
|
|
1099
|
-
const freshMap =
|
|
1658
|
+
async mergeProductsToStore(freshProducts, options) {
|
|
1659
|
+
const freshMap = new Map();
|
|
1100
1660
|
for (const p of freshProducts) {
|
|
1101
1661
|
freshMap.set(p.id, p);
|
|
1102
1662
|
}
|
|
1103
|
-
const
|
|
1663
|
+
const mergeActions = {};
|
|
1664
|
+
const changedProducts = [];
|
|
1665
|
+
const updatedList = this.store.list.map(p => {
|
|
1104
1666
|
if (freshMap.has(p.id)) {
|
|
1105
1667
|
const fresh = freshMap.get(p.id);
|
|
1106
1668
|
freshMap.delete(p.id);
|
|
1669
|
+
mergeActions[p.id] = 'update';
|
|
1670
|
+
changedProducts.push(fresh);
|
|
1107
1671
|
return fresh;
|
|
1108
1672
|
}
|
|
1109
1673
|
return p;
|
|
1110
1674
|
});
|
|
1675
|
+
|
|
1676
|
+
// freshMap 中剩余的是新商品(create 场景)
|
|
1111
1677
|
const newCount = freshMap.size;
|
|
1112
1678
|
for (const p of freshMap.values()) {
|
|
1113
1679
|
updatedList.push(p);
|
|
1680
|
+
mergeActions[p.id] = 'insert';
|
|
1681
|
+
changedProducts.push(p);
|
|
1114
1682
|
}
|
|
1115
1683
|
const updatedCount = freshProducts.length - newCount;
|
|
1684
|
+
const changedProductIds = freshProducts.map(p => p.id);
|
|
1116
1685
|
this.store.list = updatedList;
|
|
1117
1686
|
this.syncProductsMap();
|
|
1118
|
-
|
|
1119
|
-
|
|
1687
|
+
if (!options?.skipIndexDB) {
|
|
1688
|
+
await this.patchProductsInIndexDB(changedProducts, 'pubsub.mergeProductsToStore', mergeActions);
|
|
1689
|
+
}
|
|
1690
|
+
if (!options?.skipEmit) {
|
|
1691
|
+
this.core.effects.emit(_types.ProductsHooks.onProductsChanged, this.store.list);
|
|
1692
|
+
}
|
|
1693
|
+
this.logInfo('mergeProductsToStore: 合并完成', {
|
|
1120
1694
|
updatedCount,
|
|
1121
1695
|
newCount,
|
|
1122
1696
|
totalCount: this.store.list.length
|
|
1123
1697
|
});
|
|
1124
|
-
|
|
1698
|
+
return {
|
|
1699
|
+
changedProducts,
|
|
1700
|
+
mergeActions,
|
|
1701
|
+
updatedCount,
|
|
1702
|
+
newCount
|
|
1703
|
+
};
|
|
1125
1704
|
}
|
|
1705
|
+
|
|
1126
1706
|
/**
|
|
1127
1707
|
* 静默全量刷新:后台重新拉取全量 SSE 数据并更新本地
|
|
1128
1708
|
* 拿到完整数据后一次性替换 store,清除价格缓存,触发 onProductsSyncCompleted
|
|
@@ -1130,48 +1710,57 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
1130
1710
|
*/
|
|
1131
1711
|
async silentRefresh() {
|
|
1132
1712
|
const t0 = performance.now();
|
|
1133
|
-
|
|
1713
|
+
const now = Date.now();
|
|
1714
|
+
const elapsedSincePreload = now - this.lastPreloadCompletedAtMs;
|
|
1715
|
+
const elapsedSinceFullFetch = now - this.lastServerFullFetchAtMs;
|
|
1716
|
+
if (this.lastPreloadCompletedAtMs > 0 && elapsedSincePreload < PRODUCT_SILENT_REFRESH_MIN_INTERVAL_MS || this.lastServerFullFetchAtMs > 0 && elapsedSinceFullFetch < PRODUCT_SILENT_REFRESH_MIN_INTERVAL_MS) {
|
|
1717
|
+
return this.store.list;
|
|
1718
|
+
}
|
|
1719
|
+
this.logInfo('silentRefresh 开始');
|
|
1134
1720
|
try {
|
|
1135
1721
|
const products = await this.loadProductsByServer();
|
|
1136
1722
|
if (products && products.length > 0) {
|
|
1137
1723
|
this.store.list = products;
|
|
1138
1724
|
this.syncProductsMap();
|
|
1139
1725
|
this.clearPriceCache();
|
|
1140
|
-
this.core.effects.emit(
|
|
1141
|
-
await this.core.effects.emit(
|
|
1142
|
-
this.logInfo(
|
|
1726
|
+
this.core.effects.emit(_types.ProductsHooks.onProductsChanged, this.store.list);
|
|
1727
|
+
await this.core.effects.emit(_types.ProductsHooks.onProductsSyncCompleted, null);
|
|
1728
|
+
this.logInfo('silentRefresh 完成', {
|
|
1143
1729
|
productCount: products.length,
|
|
1144
1730
|
duration: `${Math.round(performance.now() - t0)}ms`
|
|
1145
1731
|
});
|
|
1146
1732
|
} else {
|
|
1147
|
-
this.logWarning(
|
|
1733
|
+
this.logWarning('silentRefresh: 服务器未返回数据');
|
|
1148
1734
|
}
|
|
1149
|
-
(0,
|
|
1735
|
+
(0, _product.perfMark)('silentRefresh', performance.now() - t0, {
|
|
1736
|
+
count: this.store.list.length
|
|
1737
|
+
});
|
|
1150
1738
|
return this.store.list;
|
|
1151
1739
|
} catch (error) {
|
|
1152
1740
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1153
|
-
this.logError(
|
|
1741
|
+
this.logError('silentRefresh 失败', {
|
|
1154
1742
|
duration: `${Math.round(performance.now() - t0)}ms`,
|
|
1155
1743
|
error: errorMessage
|
|
1156
1744
|
});
|
|
1157
1745
|
return this.store.list;
|
|
1158
1746
|
}
|
|
1159
1747
|
}
|
|
1748
|
+
|
|
1160
1749
|
/**
|
|
1161
1750
|
* 销毁同步资源(取消 pubsub 订阅、清除定时器)
|
|
1162
1751
|
*/
|
|
1163
1752
|
destroyProductSync() {
|
|
1164
|
-
var _a;
|
|
1165
1753
|
if (this.syncTimer) {
|
|
1166
1754
|
clearTimeout(this.syncTimer);
|
|
1167
|
-
this.syncTimer =
|
|
1755
|
+
this.syncTimer = undefined;
|
|
1168
1756
|
}
|
|
1169
|
-
if (
|
|
1757
|
+
if (this.productDataSource?.destroy) {
|
|
1170
1758
|
this.productDataSource.destroy();
|
|
1171
1759
|
}
|
|
1172
1760
|
this.pendingSyncMessages = [];
|
|
1173
|
-
this.logInfo(
|
|
1761
|
+
this.logInfo('destroyProductSync: 同步资源已销毁');
|
|
1174
1762
|
}
|
|
1763
|
+
|
|
1175
1764
|
/**
|
|
1176
1765
|
* 获取模块的路由定义
|
|
1177
1766
|
* Products 模块暂不提供路由,由 Server 层统一处理
|
|
@@ -1179,8 +1768,5 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
1179
1768
|
getRoutes() {
|
|
1180
1769
|
return [];
|
|
1181
1770
|
}
|
|
1182
|
-
}
|
|
1183
|
-
|
|
1184
|
-
0 && (module.exports = {
|
|
1185
|
-
ProductsModule
|
|
1186
|
-
});
|
|
1771
|
+
}
|
|
1772
|
+
exports.ProductsModule = ProductsModule;
|