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