@pisell/pisellos 0.0.479 → 0.0.481

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.
Files changed (147) hide show
  1. package/dist/core/index.d.ts +3 -2
  2. package/dist/core/index.js +7 -0
  3. package/dist/effects/index.d.ts +2 -2
  4. package/dist/effects/index.js +34 -81
  5. package/dist/model/strategy/adapter/promotion/evaluator.js +99 -26
  6. package/dist/model/strategy/adapter/walletPass/type.d.ts +9 -2
  7. package/dist/model/strategy/adapter/walletPass/utils.d.ts +6 -6
  8. package/dist/model/strategy/adapter/walletPass/utils.js +111 -72
  9. package/dist/modules/Customer/index.js +1 -1
  10. package/dist/modules/Discount/index.d.ts +6 -2
  11. package/dist/modules/Discount/index.js +14 -8
  12. package/dist/modules/Order/index.d.ts +1 -1
  13. package/dist/modules/Order/index.js +18 -13
  14. package/dist/modules/Payment/index.d.ts +4 -0
  15. package/dist/modules/Payment/index.js +774 -649
  16. package/dist/modules/Payment/walletpass.js +44 -17
  17. package/dist/modules/Product/index.d.ts +1 -1
  18. package/dist/modules/Product/types.d.ts +2 -0
  19. package/dist/modules/ProductList/index.d.ts +3 -0
  20. package/dist/modules/ProductList/index.js +9 -7
  21. package/dist/modules/Rules/index.d.ts +2 -2
  22. package/dist/modules/Rules/index.js +37 -31
  23. package/dist/modules/Rules/types.d.ts +2 -2
  24. package/dist/modules/Schedule/index.d.ts +9 -0
  25. package/dist/modules/Schedule/index.js +15 -2
  26. package/dist/plugins/app-types/app/app.d.ts +1 -0
  27. package/dist/plugins/request.d.ts +2 -0
  28. package/dist/server/index.d.ts +107 -2
  29. package/dist/server/index.js +1507 -279
  30. package/dist/server/modules/index.d.ts +6 -0
  31. package/dist/server/modules/index.js +7 -0
  32. package/dist/server/modules/menu/index.d.ts +19 -0
  33. package/dist/server/modules/menu/index.js +221 -71
  34. package/dist/server/modules/order/index.d.ts +87 -0
  35. package/dist/server/modules/order/index.js +916 -0
  36. package/dist/server/modules/order/types.d.ts +530 -0
  37. package/dist/server/modules/order/types.js +141 -0
  38. package/dist/server/modules/order/utils/filterBookings.d.ts +6 -0
  39. package/dist/server/modules/order/utils/filterBookings.js +350 -0
  40. package/dist/server/modules/order/utils/filterOrders.d.ts +15 -0
  41. package/dist/server/modules/order/utils/filterOrders.js +226 -0
  42. package/dist/server/modules/products/index.d.ts +117 -5
  43. package/dist/server/modules/products/index.js +1450 -240
  44. package/dist/server/modules/products/types.d.ts +25 -1
  45. package/dist/server/modules/products/types.js +3 -0
  46. package/dist/server/modules/resource/index.d.ts +86 -0
  47. package/dist/server/modules/resource/index.js +1128 -0
  48. package/dist/server/modules/resource/types.d.ts +121 -0
  49. package/dist/server/modules/resource/types.js +47 -0
  50. package/dist/server/modules/schedule/index.d.ts +19 -0
  51. package/dist/server/modules/schedule/index.js +229 -68
  52. package/dist/server/utils/product.d.ts +5 -0
  53. package/dist/server/utils/product.js +71 -31
  54. package/dist/solution/BookingTicket/index.d.ts +10 -2
  55. package/dist/solution/BookingTicket/index.js +41 -28
  56. package/dist/solution/BookingTicket/utils/scan/index.js +1 -1
  57. package/dist/solution/Checkout/index.d.ts +1 -0
  58. package/dist/solution/Checkout/index.js +286 -188
  59. package/dist/solution/Checkout/utils/index.d.ts +2 -1
  60. package/dist/solution/Checkout/utils/index.js +6 -4
  61. package/dist/solution/RegisterAndLogin/config.js +340 -1
  62. package/dist/solution/Sales/index.d.ts +96 -0
  63. package/dist/solution/Sales/index.js +566 -0
  64. package/dist/solution/Sales/types.d.ts +67 -0
  65. package/dist/solution/Sales/types.js +26 -0
  66. package/dist/solution/ShopDiscount/index.d.ts +1 -0
  67. package/dist/solution/ShopDiscount/index.js +35 -22
  68. package/dist/solution/ShopDiscount/types.d.ts +6 -0
  69. package/dist/solution/ShopDiscount/utils.d.ts +9 -0
  70. package/dist/solution/ShopDiscount/utils.js +21 -27
  71. package/dist/solution/index.d.ts +2 -1
  72. package/dist/solution/index.js +2 -1
  73. package/dist/types/index.d.ts +5 -0
  74. package/lib/core/index.d.ts +3 -2
  75. package/lib/core/index.js +4 -0
  76. package/lib/effects/index.d.ts +2 -2
  77. package/lib/effects/index.js +22 -31
  78. package/lib/model/strategy/adapter/promotion/evaluator.js +57 -8
  79. package/lib/model/strategy/adapter/walletPass/type.d.ts +9 -2
  80. package/lib/model/strategy/adapter/walletPass/utils.d.ts +6 -6
  81. package/lib/model/strategy/adapter/walletPass/utils.js +115 -48
  82. package/lib/modules/Customer/index.js +1 -1
  83. package/lib/modules/Discount/index.d.ts +6 -2
  84. package/lib/modules/Discount/index.js +3 -1
  85. package/lib/modules/Order/index.d.ts +1 -1
  86. package/lib/modules/Order/index.js +20 -18
  87. package/lib/modules/Payment/index.d.ts +4 -0
  88. package/lib/modules/Payment/index.js +134 -66
  89. package/lib/modules/Payment/walletpass.js +23 -4
  90. package/lib/modules/Product/index.d.ts +1 -1
  91. package/lib/modules/Product/types.d.ts +2 -0
  92. package/lib/modules/ProductList/index.d.ts +3 -0
  93. package/lib/modules/ProductList/index.js +2 -2
  94. package/lib/modules/Rules/index.d.ts +2 -2
  95. package/lib/modules/Rules/index.js +69 -73
  96. package/lib/modules/Rules/types.d.ts +2 -2
  97. package/lib/modules/Schedule/index.d.ts +9 -0
  98. package/lib/modules/Schedule/index.js +11 -0
  99. package/lib/plugins/app-types/app/app.d.ts +1 -0
  100. package/lib/plugins/request.d.ts +2 -0
  101. package/lib/server/index.d.ts +107 -2
  102. package/lib/server/index.js +773 -51
  103. package/lib/server/modules/index.d.ts +6 -0
  104. package/lib/server/modules/index.js +16 -2
  105. package/lib/server/modules/menu/index.d.ts +19 -0
  106. package/lib/server/modules/menu/index.js +121 -2
  107. package/lib/server/modules/order/index.d.ts +87 -0
  108. package/lib/server/modules/order/index.js +543 -0
  109. package/lib/server/modules/order/types.d.ts +530 -0
  110. package/lib/server/modules/order/types.js +34 -0
  111. package/lib/server/modules/order/utils/filterBookings.d.ts +6 -0
  112. package/lib/server/modules/order/utils/filterBookings.js +320 -0
  113. package/lib/server/modules/order/utils/filterOrders.d.ts +15 -0
  114. package/lib/server/modules/order/utils/filterOrders.js +197 -0
  115. package/lib/server/modules/products/index.d.ts +117 -5
  116. package/lib/server/modules/products/index.js +799 -62
  117. package/lib/server/modules/products/types.d.ts +25 -1
  118. package/lib/server/modules/products/types.js +1 -0
  119. package/lib/server/modules/resource/index.d.ts +86 -0
  120. package/lib/server/modules/resource/index.js +557 -0
  121. package/lib/server/modules/resource/types.d.ts +121 -0
  122. package/lib/server/modules/resource/types.js +35 -0
  123. package/lib/server/modules/schedule/index.d.ts +19 -0
  124. package/lib/server/modules/schedule/index.js +141 -12
  125. package/lib/server/utils/product.d.ts +5 -0
  126. package/lib/server/utils/product.js +56 -27
  127. package/lib/solution/BookingTicket/index.d.ts +10 -2
  128. package/lib/solution/BookingTicket/index.js +10 -2
  129. package/lib/solution/BookingTicket/utils/scan/index.js +0 -1
  130. package/lib/solution/Checkout/index.d.ts +1 -0
  131. package/lib/solution/Checkout/index.js +399 -331
  132. package/lib/solution/Checkout/utils/index.d.ts +2 -1
  133. package/lib/solution/Checkout/utils/index.js +6 -4
  134. package/lib/solution/RegisterAndLogin/config.js +266 -1
  135. package/lib/solution/Sales/index.d.ts +96 -0
  136. package/lib/solution/Sales/index.js +416 -0
  137. package/lib/solution/Sales/types.d.ts +67 -0
  138. package/lib/solution/Sales/types.js +35 -0
  139. package/lib/solution/ShopDiscount/index.d.ts +1 -0
  140. package/lib/solution/ShopDiscount/index.js +14 -6
  141. package/lib/solution/ShopDiscount/types.d.ts +6 -0
  142. package/lib/solution/ShopDiscount/utils.d.ts +9 -0
  143. package/lib/solution/ShopDiscount/utils.js +6 -10
  144. package/lib/solution/index.d.ts +2 -1
  145. package/lib/solution/index.js +4 -2
  146. package/lib/types/index.d.ts +5 -0
  147. package/package.json +1 -1
@@ -23,17 +23,17 @@ __export(products_exports, {
23
23
  });
24
24
  module.exports = __toCommonJS(products_exports);
25
25
  var import_BaseModule = require("../../../modules/BaseModule");
26
- var import_lodash_es = require("lodash-es");
27
26
  var import_plugins = require("../../../plugins");
28
27
  var import_types = require("./types");
29
28
  var import_product = require("../../utils/product");
30
29
  var INDEXDB_STORE_NAME = "products";
30
+ var PRODUCT_SYNC_DEBOUNCE_MS = 1e4;
31
31
  var ProductsModule = class extends import_BaseModule.BaseModule {
32
32
  constructor(name, version) {
33
33
  super(name, version);
34
34
  this.defaultName = "products";
35
35
  this.defaultVersion = "1.0.0";
36
- // IndexDBManager 实例
36
+ // LoggerManager 实例
37
37
  this.otherParams = {};
38
38
  // 商品价格缓存:Map<日期, 应用了价格的商品列表>
39
39
  this.productsPriceCache = /* @__PURE__ */ new Map();
@@ -44,6 +44,7 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
44
44
  this.formatters = [];
45
45
  // 是否已注册内置价格格式化器
46
46
  this.isPriceFormatterRegistered = false;
47
+ this.pendingSyncMessages = [];
47
48
  }
48
49
  async initialize(core, options) {
49
50
  var _a;
@@ -63,6 +64,7 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
63
64
  if (appPlugin) {
64
65
  const app = appPlugin.getApp();
65
66
  this.dbManager = app.dbManager;
67
+ this.logger = app.logger;
66
68
  if (this.dbManager) {
67
69
  console.log("[Products] IndexDB Manager 已初始化");
68
70
  } else {
@@ -70,6 +72,64 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
70
72
  }
71
73
  }
72
74
  this.registerBuiltinPriceFormatter();
75
+ this.initProductDataSource();
76
+ this.setupProductSync();
77
+ this.logInfo("模块初始化完成", {
78
+ hasDbManager: !!this.dbManager,
79
+ hasLogger: !!this.logger,
80
+ initialProductCount: this.store.list.length
81
+ });
82
+ }
83
+ /**
84
+ * 记录信息日志
85
+ * @param title 日志标题
86
+ * @param metadata 日志元数据
87
+ */
88
+ logInfo(title, metadata) {
89
+ try {
90
+ if (this.logger) {
91
+ this.logger.addLog({
92
+ type: "info",
93
+ title: `[ProductsModule] ${title}`,
94
+ metadata: metadata || {}
95
+ });
96
+ }
97
+ } catch {
98
+ }
99
+ }
100
+ /**
101
+ * 记录警告日志
102
+ * @param title 日志标题
103
+ * @param metadata 日志元数据
104
+ */
105
+ logWarning(title, metadata) {
106
+ try {
107
+ if (this.logger) {
108
+ this.logger.addLog({
109
+ type: "warning",
110
+ title: `[ProductsModule] ${title}`,
111
+ metadata: metadata || {}
112
+ });
113
+ }
114
+ } catch {
115
+ }
116
+ }
117
+ /**
118
+ * 记录错误日志
119
+ * @param title 日志标题
120
+ * @param metadata 日志元数据
121
+ */
122
+ logError(title, metadata) {
123
+ try {
124
+ if (this.logger) {
125
+ this.logger.addLog({
126
+ type: "error",
127
+ title: `[ProductsModule] ${title}`,
128
+ metadata: metadata || {}
129
+ });
130
+ }
131
+ } catch {
132
+ }
73
133
  }
74
134
  /**
75
135
  * 加载商品价格(原始方法,不带缓存)
@@ -80,21 +140,45 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
80
140
  customer_id,
81
141
  schedule_date
82
142
  }) {
83
- const productsData = await this.request.post(
84
- `/product/query/price`,
85
- {
86
- ids,
87
- customer_id,
88
- schedule_date
89
- },
90
- {
91
- cache: {
92
- mode: import_plugins.RequestModeENUM.REMOTE_LOCAL,
93
- type: "indexDB"
143
+ var _a;
144
+ this.logInfo("开始加载商品价格", {
145
+ productCount: ids.length,
146
+ schedule_date,
147
+ customer_id
148
+ });
149
+ const startTime = Date.now();
150
+ try {
151
+ const productsData = await this.request.post(
152
+ `/product/query/price`,
153
+ {
154
+ ids,
155
+ customer_id,
156
+ schedule_date
157
+ },
158
+ {
159
+ cache: {
160
+ mode: import_plugins.RequestModeENUM.REMOTE_LOCAL,
161
+ type: "memory"
162
+ }
94
163
  }
95
- }
96
- );
97
- return productsData.data;
164
+ );
165
+ const duration = Date.now() - startTime;
166
+ this.logInfo("加载商品价格成功", {
167
+ productCount: ids.length,
168
+ priceDataCount: ((_a = productsData.data) == null ? void 0 : _a.length) ?? 0,
169
+ duration: `${duration}ms`
170
+ });
171
+ return productsData.data;
172
+ } catch (error) {
173
+ const duration = Date.now() - startTime;
174
+ const errorMessage = error instanceof Error ? error.message : String(error);
175
+ this.logError("加载商品价格失败", {
176
+ productCount: ids.length,
177
+ duration: `${duration}ms`,
178
+ error: errorMessage
179
+ });
180
+ return [];
181
+ }
98
182
  }
99
183
  /**
100
184
  * 获取应用了价格的商品列表(带缓存)
@@ -102,53 +186,174 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
102
186
  * 缓存的是已经应用了价格的完整商品列表,避免重复转换
103
187
  * @param schedule_date 日期
104
188
  * @param extraContext 额外的上下文数据(可选,由 Server 层传入)
189
+ * @param options 可选参数
190
+ * @param options.changedIds 变更的商品 IDs,非空时仅对这些商品增量执行 prepare 并更新缓存
105
191
  * @returns 应用了价格的商品列表
106
192
  */
107
- async getProductsWithPrice(schedule_date, extraContext) {
193
+ async getProductsWithPrice(schedule_date, extraContext, options) {
194
+ const t0 = performance.now();
108
195
  const cacheKey = schedule_date;
196
+ const changedIds = options == null ? void 0 : options.changedIds;
109
197
  if (this.productsPriceCache.has(cacheKey)) {
110
- console.log(`[ProductsModule] 💰 商品价格缓存命中: ${cacheKey}`);
111
- return this.productsPriceCache.get(cacheKey);
198
+ const cachedProducts = this.productsPriceCache.get(cacheKey);
199
+ if (changedIds && changedIds.length > 0) {
200
+ this.logInfo("商品价格缓存命中,增量更新变更商品", {
201
+ cacheKey,
202
+ changedIds,
203
+ cachedProductCount: cachedProducts.length
204
+ });
205
+ try {
206
+ const updatedProducts = await this.prepareProductsWithPrice(
207
+ schedule_date,
208
+ extraContext,
209
+ { productIds: changedIds }
210
+ );
211
+ if (updatedProducts.length > 0) {
212
+ const updatedMap = new Map(updatedProducts.map((p) => [p.id, p]));
213
+ const mergedCache = [];
214
+ for (const p of cachedProducts) {
215
+ if (updatedMap.has(p.id)) {
216
+ mergedCache.push(updatedMap.get(p.id));
217
+ updatedMap.delete(p.id);
218
+ } else {
219
+ mergedCache.push(p);
220
+ }
221
+ }
222
+ for (const p of updatedMap.values()) {
223
+ mergedCache.push(p);
224
+ }
225
+ this.productsPriceCache.set(cacheKey, mergedCache);
226
+ this.logInfo("增量更新完成", {
227
+ cacheKey,
228
+ changedCount: updatedProducts.length,
229
+ totalCount: mergedCache.length
230
+ });
231
+ (0, import_product.perfMark)("getProductsWithPrice(incrementalUpdate)", performance.now() - t0, {
232
+ cacheKey,
233
+ changedCount: changedIds.length,
234
+ count: mergedCache.length
235
+ });
236
+ return mergedCache;
237
+ }
238
+ } catch (error) {
239
+ const errorMessage = error instanceof Error ? error.message : String(error);
240
+ this.logError("增量更新失败,返回现有缓存", {
241
+ cacheKey,
242
+ changedIds,
243
+ error: errorMessage
244
+ });
245
+ }
246
+ (0, import_product.perfMark)("getProductsWithPrice(incrementalUpdate)", performance.now() - t0, {
247
+ cacheKey,
248
+ changedCount: changedIds.length,
249
+ count: cachedProducts.length
250
+ });
251
+ return cachedProducts;
252
+ }
253
+ (0, import_product.perfMark)("getProductsWithPrice(cacheHit)", performance.now() - t0, {
254
+ cacheKey,
255
+ count: cachedProducts.length
256
+ });
257
+ this.logInfo("商品价格缓存命中", {
258
+ cacheKey,
259
+ productCount: cachedProducts.length
260
+ });
261
+ return cachedProducts;
112
262
  }
113
- console.log(`[ProductsModule] 🌐 获取商品并应用价格: ${cacheKey}`);
263
+ this.logInfo("商品价格缓存未命中,准备获取", { cacheKey });
114
264
  const result = await this.prepareProductsWithPrice(schedule_date, extraContext);
115
265
  this.productsPriceCache.set(cacheKey, result);
116
- console.log(`[ProductsModule] ✅ 商品价格已缓存: ${cacheKey}, 共 ${result.length} 个商品`);
266
+ this.logInfo("商品价格已缓存", {
267
+ cacheKey,
268
+ productCount: result.length
269
+ });
117
270
  this.cleanExpiredPriceCache();
271
+ (0, import_product.perfMark)("getProductsWithPrice(cacheMiss)", performance.now() - t0, {
272
+ cacheKey,
273
+ count: result.length
274
+ });
118
275
  return result;
119
276
  }
120
277
  /**
121
278
  * 准备带价格的商品数据(通过格式化器流程处理)
122
279
  * @param schedule_date 日期
123
280
  * @param extraContext 额外的上下文数据(可选)
124
- * @returns 完整处理后的商品列表
281
+ * @param options 可选参数
282
+ * @param options.productIds 指定商品 IDs,仅处理这些商品;不传则处理全量
283
+ * @returns 处理后的商品列表
125
284
  * @private
126
285
  */
127
- async prepareProductsWithPrice(schedule_date, extraContext) {
286
+ async prepareProductsWithPrice(schedule_date, extraContext, options) {
287
+ var _a, _b;
288
+ const tTotal = performance.now();
289
+ const targetIds = options == null ? void 0 : options.productIds;
290
+ const isIncremental = targetIds && targetIds.length > 0;
291
+ this.logInfo("prepareProductsWithPrice 开始处理", {
292
+ schedule_date,
293
+ mode: isIncremental ? "incremental" : "full",
294
+ targetIdsCount: targetIds == null ? void 0 : targetIds.length
295
+ });
128
296
  try {
129
- const allProducts = await this.getProducts();
130
- console.log(`[ProductsModule] 🌐 开始获取商品报价单价格`);
297
+ let products;
298
+ let ids;
299
+ if (isIncremental) {
300
+ const idSet = new Set(targetIds);
301
+ products = this.getProductsRef().filter((p) => idSet.has(p.id));
302
+ ids = products.map((p) => p.id);
303
+ } else {
304
+ products = this.getProductsRef();
305
+ const tIds = performance.now();
306
+ ids = new Array(products.length);
307
+ for (let i = 0; i < products.length; i++) {
308
+ ids[i] = products[i].id;
309
+ }
310
+ (0, import_product.perfMark)("prepareProducts.extractIds", performance.now() - tIds, { count: ids.length });
311
+ }
312
+ this.logInfo("获取到商品列表", { productCount: products.length });
313
+ const tPrice = performance.now();
131
314
  const priceData = await this.loadProductsPrice({
132
- ids: allProducts.map((product) => product.id).sort((a, b) => a - b),
315
+ ids,
133
316
  schedule_date
134
317
  });
135
- console.log(`[ProductsModule] 🌐 获取商品报价单价格成功`, priceData);
318
+ (0, import_product.perfMark)("prepareProducts.loadPrice", performance.now() - tPrice, { count: ids.length });
319
+ this.logInfo("获取商品报价单价格成功", { priceDataCount: (priceData == null ? void 0 : priceData.length) ?? 0 });
136
320
  const context = {
137
321
  schedule_date,
138
322
  priceData,
323
+ locale: (_b = (_a = this.core) == null ? void 0 : _a.context) == null ? void 0 : _b.locale,
139
324
  ...extraContext
140
- // 合并 Server 层传入的额外数据(如 scheduleList)
141
325
  };
142
- console.log(`[ProductsModule] 🌐 通过格式化器流程处理商品(包括价格应用、字段扩展等)`);
143
- const processedProducts = await this.applyFormatters(allProducts, context);
144
- console.log(`[ProductsModule] 🌐 通过格式化器流程处理商品(包括价格应用、字段扩展等)成功`, processedProducts);
145
- await this.core.effects.emit(
146
- import_types.ProductsHooks.onProductsPriceApplied,
147
- processedProducts
148
- );
326
+ const tFormat = performance.now();
327
+ const processedProducts = await this.applyFormatters(products, context);
328
+ (0, import_product.perfMark)("prepareProducts.applyFormatters", performance.now() - tFormat, {
329
+ count: products.length,
330
+ formatterCount: this.formatters.length
331
+ });
332
+ this.logInfo("prepareProductsWithPrice 处理完成", {
333
+ mode: isIncremental ? "incremental" : "full",
334
+ originalProductCount: products.length,
335
+ processedProductCount: processedProducts.length,
336
+ formatterCount: this.formatters.length
337
+ });
338
+ if (!isIncremental) {
339
+ await this.core.effects.emit(
340
+ import_types.ProductsHooks.onProductsPriceApplied,
341
+ processedProducts
342
+ );
343
+ }
344
+ (0, import_product.perfMark)("prepareProductsWithPrice", performance.now() - tTotal, {
345
+ productCount: products.length,
346
+ formatterCount: this.formatters.length,
347
+ mode: isIncremental ? "incremental" : "full"
348
+ });
149
349
  return processedProducts;
150
350
  } catch (err) {
351
+ const errorMessage = err instanceof Error ? err.message : String(err);
151
352
  console.log(`[ProductsModule] 🌐 ERROR`, err);
353
+ this.logError("prepareProductsWithPrice 处理失败", {
354
+ schedule_date,
355
+ error: errorMessage
356
+ });
152
357
  }
153
358
  return [];
154
359
  }
@@ -163,18 +368,37 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
163
368
  let result = products;
164
369
  if (this.formatters.length === 0) {
165
370
  console.warn("[ProductsModule] ⚠️ 没有注册任何格式化器");
371
+ this.logWarning("没有注册任何格式化器", { productCount: products.length });
166
372
  return result;
167
373
  }
374
+ this.logInfo("开始应用格式化器", {
375
+ productCount: products.length,
376
+ formatterCount: this.formatters.length
377
+ });
168
378
  for (let i = 0; i < this.formatters.length; i++) {
169
379
  const formatter = this.formatters[i];
170
380
  try {
171
- console.log(`[ProductsModule] 📝 应用格式化器 ${i + 1}/${this.formatters.length}`);
381
+ const tF = performance.now();
172
382
  result = await formatter(result, context);
383
+ (0, import_product.perfMark)(`applyFormatters[${i}]`, performance.now() - tF, {
384
+ index: i,
385
+ total: this.formatters.length,
386
+ productCount: result.length
387
+ });
173
388
  } catch (error) {
389
+ const errorMessage = error instanceof Error ? error.message : String(error);
174
390
  console.error(`[ProductsModule] ❌ 格式化器 ${i + 1} 执行失败:`, error);
391
+ this.logError(`格式化器 ${i + 1} 执行失败`, {
392
+ formatterIndex: i + 1,
393
+ totalFormatters: this.formatters.length,
394
+ error: errorMessage
395
+ });
175
396
  }
176
397
  }
177
- console.log(`[ProductsModule] ✅ 所有格式化器已应用,共 ${this.formatters.length} 个`);
398
+ this.logInfo("所有格式化器已应用", {
399
+ formatterCount: this.formatters.length,
400
+ resultProductCount: result.length
401
+ });
178
402
  return result;
179
403
  }
180
404
  /**
@@ -195,10 +419,14 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
195
419
  console.log(`[ProductsModule] 💰 应用价格数据到 ${products.length} 个商品`);
196
420
  return (0, import_product.applyPriceDataToProducts)(products, context.priceData);
197
421
  };
422
+ const i18nFormatter = (products, context) => {
423
+ return (0, import_product.applyI18nToProducts)(products, context.locale);
424
+ };
198
425
  const detailValueFormatter = (products, context) => {
199
426
  return (0, import_product.applyDetailValueToProducts)(products, context);
200
427
  };
201
428
  this.formatters.unshift(priceFormatter);
429
+ this.formatters.push(i18nFormatter);
202
430
  this.formatters.push(detailValueFormatter);
203
431
  this.isPriceFormatterRegistered = true;
204
432
  console.log("[ProductsModule] ✅ 内置价格格式化器已注册(第 1 个)");
@@ -264,10 +492,45 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
264
492
  console.log("[ProductsModule] 🗑️ 商品价格缓存已清空");
265
493
  }
266
494
  /**
267
- * 加载完整商品列表通过接口(包含所有详细数据)
495
+ * 通过 ProductDataSource SSE 加载完整商品列表
496
+ */
497
+ async loadProductsByServer() {
498
+ if (!this.productDataSource) {
499
+ this.logWarning("loadProductsByServer: ProductDataSource 不可用");
500
+ return [];
501
+ }
502
+ this.logInfo("开始通过 DataSource SSE 加载商品列表");
503
+ const t0 = performance.now();
504
+ try {
505
+ const tSSE = performance.now();
506
+ const productList = await this.productDataSource.run({ sse: {} });
507
+ const list = productList || [];
508
+ (0, import_product.perfMark)("loadProductsByServer.SSE", performance.now() - tSSE, { count: list.length });
509
+ this.logInfo("通过 DataSource SSE 加载商品列表成功", {
510
+ productCount: list.length,
511
+ duration: `${Math.round(performance.now() - t0)}ms`
512
+ });
513
+ await this.saveProductsToIndexDB(list);
514
+ await this.core.effects.emit(import_types.ProductsHooks.onProductsLoaded, list);
515
+ (0, import_product.perfMark)("loadProductsByServer", performance.now() - t0, { count: list.length });
516
+ return list;
517
+ } catch (error) {
518
+ const duration = Math.round(performance.now() - t0);
519
+ const errorMessage = error instanceof Error ? error.message : String(error);
520
+ console.error("[Products] 加载商品数据失败:", error);
521
+ this.logError("通过 DataSource SSE 加载商品列表失败", {
522
+ duration: `${duration}ms`,
523
+ error: errorMessage
524
+ });
525
+ return [];
526
+ }
527
+ }
528
+ /**
529
+ * 纯请求方法:通过 HTTP 接口获取商品列表(无副作用,不触发事件、不写 IndexDB)
268
530
  * @param params 查询参数
531
+ * @returns 商品列表
269
532
  */
270
- async loadProductsByServer(params) {
533
+ async fetchProductsByHttp(params) {
271
534
  var _a, _b;
272
535
  const {
273
536
  category_ids = [],
@@ -276,6 +539,13 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
276
539
  customer_id,
277
540
  cacheId
278
541
  } = params || {};
542
+ this.logInfo("fetchProductsByHttp: 开始请求", {
543
+ categoryIdsCount: category_ids.length,
544
+ productIdsCount: product_ids.length,
545
+ collectionCount: Array.isArray(collection) ? collection.length : collection ? 1 : 0,
546
+ customer_id
547
+ });
548
+ const startTime = Date.now();
279
549
  try {
280
550
  const productsData = await this.request.post(
281
551
  `/product/query`,
@@ -290,20 +560,18 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
290
560
  "event_item"
291
561
  ],
292
562
  force_ignore_cache_flag: 1,
293
- // 获取所有详细信息
294
563
  with: [
295
564
  "category",
296
565
  "collection",
297
566
  "resourceRelation",
298
- // 套餐
299
567
  "bundleGroup.bundleItem",
300
- // 单规格
301
568
  "optionGroup.optionItem",
302
- // 组合规格
303
569
  "variantGroup.variantItem"
304
570
  ],
305
571
  open_deposit: 1,
572
+ is_append_product_description: 1,
306
573
  with_count: ["bundleGroup", "optionGroup"],
574
+ with_category_tree: 1,
307
575
  status: "published",
308
576
  num: 500,
309
577
  skip: 1,
@@ -313,24 +581,54 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
313
581
  front_end_cache_id: cacheId,
314
582
  application_code: (_a = this.otherParams) == null ? void 0 : _a.channel
315
583
  },
316
- { useCache: true }
584
+ { cache: void 0 }
317
585
  );
318
- await this.saveProductsToIndexDB(((_b = productsData == null ? void 0 : productsData.data) == null ? void 0 : _b.list) || []);
319
- await this.core.effects.emit(
320
- import_types.ProductsHooks.onProductsLoaded,
321
- productsData.data.list
322
- );
323
- return productsData.data.list;
586
+ const productList = ((_b = productsData == null ? void 0 : productsData.data) == null ? void 0 : _b.list) || [];
587
+ const duration = Date.now() - startTime;
588
+ this.logInfo("fetchProductsByHttp: 请求成功", {
589
+ productCount: productList.length,
590
+ duration: `${duration}ms`
591
+ });
592
+ return productList;
324
593
  } catch (error) {
325
- console.error("[Products] 加载商品数据失败:", error);
594
+ const duration = Date.now() - startTime;
595
+ const errorMessage = error instanceof Error ? error.message : String(error);
596
+ console.error("[Products] fetchProductsByHttp 失败:", error);
597
+ this.logError("fetchProductsByHttp: 请求失败", {
598
+ duration: `${duration}ms`,
599
+ error: errorMessage
600
+ });
326
601
  return [];
327
602
  }
328
603
  }
329
604
  /**
330
- * 获取商品列表(从缓存)
605
+ * 加载完整商品列表通过接口(包含所有详细数据)
606
+ * 包含副作用:保存到 IndexDB + 触发 onProductsLoaded 事件
607
+ * @param params 查询参数
608
+ */
609
+ async loadProductsByServerHttp(params) {
610
+ const productList = await this.fetchProductsByHttp(params);
611
+ if (productList.length > 0) {
612
+ await this.saveProductsToIndexDB(productList);
613
+ await this.core.effects.emit(import_types.ProductsHooks.onProductsLoaded, productList);
614
+ }
615
+ return productList;
616
+ }
617
+ /**
618
+ * 获取商品列表(深拷贝,供外部安全使用)
331
619
  */
332
620
  async getProducts() {
333
- return (0, import_lodash_es.cloneDeep)(this.store.list);
621
+ const t0 = performance.now();
622
+ const result = structuredClone(this.store.list);
623
+ (0, import_product.perfMark)("ProductsModule.getProducts(structuredClone)", performance.now() - t0, { count: result.length });
624
+ return result;
625
+ }
626
+ /**
627
+ * 内部获取商品列表的直接引用(无拷贝)
628
+ * 仅供内部 formatter 流程使用,因为 formatter 会创建新对象
629
+ */
630
+ getProductsRef() {
631
+ return this.store.list;
334
632
  }
335
633
  /**
336
634
  * 根据ID获取单个商品(从内存缓存)
@@ -338,23 +636,80 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
338
636
  */
339
637
  async getProductById(id) {
340
638
  const product = this.store.map.get(id);
341
- return product ? (0, import_lodash_es.cloneDeep)(product) : void 0;
639
+ return product ? structuredClone(product) : void 0;
640
+ }
641
+ /**
642
+ * 根据 ID 列表删除商品(用于 pubsub 同步删除场景)
643
+ * 同时更新 store.list、store.map、IndexDB 和价格缓存
644
+ */
645
+ async removeProductsByIds(ids) {
646
+ const idSet = new Set(ids);
647
+ this.logInfo("removeProductsByIds", { ids, count: ids.length });
648
+ this.store.list = this.store.list.filter((p) => !idSet.has(p.id));
649
+ for (const id of ids) {
650
+ this.store.map.delete(id);
651
+ }
652
+ if (this.dbManager) {
653
+ try {
654
+ for (const id of ids) {
655
+ await this.dbManager.delete(INDEXDB_STORE_NAME, id);
656
+ }
657
+ } catch (error) {
658
+ const errorMessage = error instanceof Error ? error.message : String(error);
659
+ this.logError("removeProductsByIds: IndexDB 删除失败", { ids, error: errorMessage });
660
+ }
661
+ }
662
+ for (const [dateKey, cachedProducts] of this.productsPriceCache.entries()) {
663
+ this.productsPriceCache.set(
664
+ dateKey,
665
+ cachedProducts.filter((p) => !idSet.has(p.id))
666
+ );
667
+ }
668
+ this.core.effects.emit(import_types.ProductsHooks.onProductsChanged, this.store.list);
669
+ this.logInfo("removeProductsByIds 完成", { remaining: this.store.list.length });
670
+ }
671
+ /**
672
+ * 重新从服务器加载全量商品列表并更新本地 store
673
+ * 用于 pubsub 同步 create / update / batch_update 场景
674
+ */
675
+ async refreshProducts() {
676
+ const tTotal = performance.now();
677
+ this.logInfo("refreshProducts 开始");
678
+ const products = await this.loadProductsByServer();
679
+ if (products && products.length > 0) {
680
+ this.store.list = products;
681
+ this.syncProductsMap();
682
+ this.core.effects.emit(import_types.ProductsHooks.onProductsChanged, this.store.list);
683
+ this.logInfo("refreshProducts 完成", { productCount: products.length });
684
+ } else {
685
+ this.logWarning("refreshProducts: 服务器未返回数据");
686
+ }
687
+ (0, import_product.perfMark)("refreshProducts", performance.now() - tTotal, { count: this.store.list.length });
688
+ return this.store.list;
342
689
  }
343
690
  /**
344
691
  * 清空缓存
345
692
  */
346
693
  async clear() {
694
+ this.logInfo("开始清空缓存", {
695
+ currentProductCount: this.store.list.length,
696
+ priceCacheCount: this.productsPriceCache.size
697
+ });
347
698
  this.store.list = [];
348
699
  this.store.map.clear();
349
700
  if (this.dbManager) {
350
701
  try {
351
702
  await this.dbManager.clear(INDEXDB_STORE_NAME);
352
703
  console.log("[Products] IndexDB 缓存已清空");
704
+ this.logInfo("IndexDB 缓存已清空");
353
705
  } catch (error) {
706
+ const errorMessage = error instanceof Error ? error.message : String(error);
354
707
  console.error("[Products] 清空 IndexDB 缓存失败:", error);
708
+ this.logError("清空 IndexDB 缓存失败", { error: errorMessage });
355
709
  }
356
710
  }
357
711
  console.log("[Products] 缓存已清空");
712
+ this.logInfo("缓存清空完成");
358
713
  }
359
714
  /**
360
715
  * 从 IndexDB 加载商品数据
@@ -362,13 +717,21 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
362
717
  */
363
718
  async loadProductsFromIndexDB() {
364
719
  if (!this.dbManager) {
720
+ this.logWarning("loadProductsFromIndexDB: dbManager 不可用");
365
721
  return [];
366
722
  }
367
723
  try {
724
+ const t0 = performance.now();
368
725
  const products = await this.dbManager.getAll(INDEXDB_STORE_NAME);
726
+ (0, import_product.perfMark)("loadProductsFromIndexDB", performance.now() - t0, { count: (products == null ? void 0 : products.length) ?? 0 });
727
+ this.logInfo("从 IndexDB 加载商品数据", {
728
+ productCount: (products == null ? void 0 : products.length) ?? 0
729
+ });
369
730
  return products || [];
370
731
  } catch (error) {
732
+ const errorMessage = error instanceof Error ? error.message : String(error);
371
733
  console.error("[Products] 从 IndexDB 读取数据失败:", error);
734
+ this.logError("从 IndexDB 读取数据失败", { error: errorMessage });
372
735
  return [];
373
736
  }
374
737
  }
@@ -378,19 +741,26 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
378
741
  */
379
742
  async saveProductsToIndexDB(products) {
380
743
  if (!this.dbManager) {
744
+ this.logWarning("saveProductsToIndexDB: dbManager 不可用");
381
745
  return;
382
746
  }
747
+ this.logInfo("开始保存商品数据到 IndexDB", { productCount: products.length });
383
748
  try {
749
+ const t0 = performance.now();
384
750
  await this.dbManager.clear(INDEXDB_STORE_NAME);
385
- const savePromises = products.map(
386
- (product) => this.dbManager.add(INDEXDB_STORE_NAME, product)
387
- );
388
- await Promise.all(savePromises);
751
+ await this.dbManager.bulkUpdate(INDEXDB_STORE_NAME, products);
752
+ (0, import_product.perfMark)("saveProductsToIndexDB", performance.now() - t0, { count: products.length });
389
753
  console.log(
390
754
  `[Products] 已将 ${products.length} 个商品平铺保存到 IndexDB`
391
755
  );
756
+ this.logInfo("商品数据已保存到 IndexDB", { productCount: products.length });
392
757
  } catch (error) {
758
+ const errorMessage = error instanceof Error ? error.message : String(error);
393
759
  console.error("[Products] 保存数据到 IndexDB 失败:", error);
760
+ this.logError("保存数据到 IndexDB 失败", {
761
+ productCount: products.length,
762
+ error: errorMessage
763
+ });
394
764
  }
395
765
  }
396
766
  /**
@@ -399,11 +769,12 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
399
769
  * @private
400
770
  */
401
771
  syncProductsMap() {
772
+ const t0 = performance.now();
402
773
  this.store.map.clear();
403
774
  for (const product of this.store.list) {
404
775
  this.store.map.set(product.id, product);
405
776
  }
406
- console.log(`[Products] Map 缓存已同步,共 ${this.store.map.size} 个商品`);
777
+ (0, import_product.perfMark)("syncProductsMap", performance.now() - t0, { count: this.store.map.size });
407
778
  }
408
779
  /**
409
780
  * 预加载模块数据(统一接口)
@@ -411,29 +782,395 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
411
782
  */
412
783
  async preload() {
413
784
  console.log("[Products] 开始预加载数据...");
785
+ const tTotal = performance.now();
786
+ this.logInfo("开始预加载数据");
414
787
  try {
788
+ const tIndexDB = performance.now();
415
789
  const cachedData = await this.loadProductsFromIndexDB();
790
+ (0, import_product.perfMark)("preload.loadFromIndexDB", performance.now() - tIndexDB, { count: (cachedData == null ? void 0 : cachedData.length) ?? 0 });
416
791
  if (cachedData && cachedData.length > 0) {
417
792
  console.log(`[Products] 从 IndexDB 加载了 ${cachedData.length} 个商品`);
418
- this.store.list = (0, import_lodash_es.cloneDeep)(cachedData);
793
+ this.store.list = cachedData;
794
+ const tSync = performance.now();
419
795
  this.syncProductsMap();
796
+ (0, import_product.perfMark)("preload.syncProductsMap", performance.now() - tSync, { count: cachedData.length });
420
797
  this.core.effects.emit(
421
798
  import_types.ProductsHooks.onProductsChanged,
422
799
  this.store.list
423
800
  );
801
+ (0, import_product.perfMark)("preload(IndexDB)", performance.now() - tTotal, {
802
+ count: cachedData.length,
803
+ source: "IndexDB"
804
+ });
805
+ this.logInfo("预加载完成(从 IndexDB)", {
806
+ productCount: cachedData.length,
807
+ duration: `${Math.round(performance.now() - tTotal)}ms`,
808
+ source: "IndexDB"
809
+ });
424
810
  return;
425
811
  }
426
812
  console.log("[Products] IndexDB 中没有缓存数据,从服务器加载...");
813
+ this.logInfo("IndexDB 中没有缓存数据,准备从服务器加载");
427
814
  } catch (error) {
815
+ const errorMessage = error instanceof Error ? error.message : String(error);
428
816
  console.warn("[Products] 从 IndexDB 加载数据失败:", error);
817
+ this.logWarning("从 IndexDB 加载数据失败,准备从服务器加载", { error: errorMessage });
429
818
  }
819
+ const tServer = performance.now();
430
820
  const products = await this.loadProductsByServer();
821
+ (0, import_product.perfMark)("preload.loadFromServer", performance.now() - tServer, { count: (products == null ? void 0 : products.length) ?? 0 });
431
822
  if (products && products.length > 0) {
432
- await this.saveProductsToIndexDB(products);
433
- this.store.list = (0, import_lodash_es.cloneDeep)(products);
823
+ this.store.list = products;
824
+ const tSync = performance.now();
434
825
  this.syncProductsMap();
826
+ (0, import_product.perfMark)("preload.syncProductsMap", performance.now() - tSync, { count: products.length });
435
827
  this.core.effects.emit(import_types.ProductsHooks.onProductsChanged, this.store.list);
828
+ (0, import_product.perfMark)("preload(Server)", performance.now() - tTotal, {
829
+ count: products.length,
830
+ source: "Server"
831
+ });
832
+ this.logInfo("预加载完成(从服务器)", {
833
+ productCount: products.length,
834
+ duration: `${Math.round(performance.now() - tTotal)}ms`,
835
+ source: "Server"
836
+ });
837
+ } else {
838
+ (0, import_product.perfMark)("preload(empty)", performance.now() - tTotal, { source: "empty" });
839
+ this.logWarning("预加载完成但未获取到数据", {
840
+ duration: `${Math.round(performance.now() - tTotal)}ms`
841
+ });
842
+ }
843
+ }
844
+ // =============================================
845
+ // 商品数据同步(pubsub)
846
+ // =============================================
847
+ /**
848
+ * 初始化 ProductDataSource 实例
849
+ * 与 pubsub 订阅和数据获取分开,仅负责创建实例
850
+ */
851
+ initProductDataSource() {
852
+ var _a, _b;
853
+ const ProductDataSourceClass = (_b = (_a = this.core.serverOptions) == null ? void 0 : _a.All_DATA_SOURCES) == null ? void 0 : _b.ProductDataSource;
854
+ if (!ProductDataSourceClass) {
855
+ this.logWarning("initProductDataSource: ProductDataSource 不可用");
856
+ return;
857
+ }
858
+ this.productDataSource = new ProductDataSourceClass();
859
+ this.logInfo("ProductDataSource 实例已创建");
860
+ }
861
+ /**
862
+ * 初始化 pubsub 订阅,监听管理后台商品变更
863
+ * 仅负责订阅 product / product_quotation 频道,消息通过防抖合并后批量处理
864
+ * 数据获取由 loadProductsByServer 单独负责
865
+ */
866
+ setupProductSync() {
867
+ if (!this.productDataSource) {
868
+ this.logWarning("setupProductSync: ProductDataSource 不可用,跳过同步初始化");
869
+ return;
870
+ }
871
+ const createHandler = (channelKey) => (message) => {
872
+ const data = (message == null ? void 0 : message.data) || message;
873
+ if (!data)
874
+ return;
875
+ console.log(`[ProductsModule] 收到同步消息 [${channelKey}]:`, data);
876
+ this.logInfo("收到同步消息", {
877
+ channelKey,
878
+ action: data.action,
879
+ id: data.id,
880
+ action_filed: data.action_filed
881
+ });
882
+ this.pendingSyncMessages.push({ ...data, _channelKey: channelKey });
883
+ if (this.syncTimer) {
884
+ clearTimeout(this.syncTimer);
885
+ }
886
+ this.syncTimer = setTimeout(() => {
887
+ this.processProductSyncMessages();
888
+ }, PRODUCT_SYNC_DEBOUNCE_MS);
889
+ };
890
+ this.productDataSource.run({
891
+ pubsub: {
892
+ callback: (res) => {
893
+ console.log("sse_products_callback", res);
894
+ this.logInfo("sse_products_callback: 收到同步消息", {
895
+ data: res == null ? void 0 : res.data
896
+ });
897
+ if (!(res == null ? void 0 : res.data))
898
+ return;
899
+ const data = res.data;
900
+ const channelKey = data.module || "product";
901
+ createHandler(channelKey)(data);
902
+ }
903
+ }
904
+ }).catch((err) => {
905
+ this.logError("setupProductSync: DataSource run 出错", {
906
+ error: err instanceof Error ? err.message : String(err)
907
+ });
908
+ });
909
+ this.logInfo("setupProductSync: pubsub 订阅已建立");
910
+ }
911
+ /**
912
+ * 处理防抖后的同步消息批次
913
+ *
914
+ * product 模块:
915
+ * - operation === 'delete' → 本地删除
916
+ * - change_types 包含 price → 仅收集变更 IDs(不拉商品数据)
917
+ * - 有 body → body 完整数据直接覆盖本地
918
+ * - 其他 → SSE 增量拉取
919
+ *
920
+ * product_collection / product_category:
921
+ * - 按 relation_product_ids SSE 拉取受影响商品
922
+ *
923
+ * product_quotation:
924
+ * - 报价单变更影响范围大,直接清除价格缓存走全量重建
925
+ *
926
+ * 处理完成后 emit onProductsSyncCompleted(携带 changedIds),
927
+ * Server 层监听该事件后对变更商品增量执行 prepareProductsWithPrice 并更新缓存
928
+ */
929
+ async processProductSyncMessages() {
930
+ var _a, _b, _c, _d, _e, _f;
931
+ const messages = [...this.pendingSyncMessages];
932
+ this.pendingSyncMessages = [];
933
+ if (messages.length === 0)
934
+ return;
935
+ this.logInfo("processProductSyncMessages: 开始处理", { count: messages.length });
936
+ const deleteIds = [];
937
+ const bodyUpdates = /* @__PURE__ */ new Map();
938
+ const sseRefreshIds = [];
939
+ const priceRefreshIds = [];
940
+ let shouldClearPriceCache = false;
941
+ for (const msg of messages) {
942
+ const channelKey = msg._channelKey || msg.module || "product";
943
+ if (channelKey === "product") {
944
+ if ((_a = msg.relation_product_ids) == null ? void 0 : _a.length) {
945
+ sseRefreshIds.push(...msg.relation_product_ids);
946
+ }
947
+ if (msg.operation === "delete" || msg.action === "delete") {
948
+ if ((_b = msg.ids) == null ? void 0 : _b.length)
949
+ deleteIds.push(...msg.ids);
950
+ else if (msg.id)
951
+ deleteIds.push(msg.id);
952
+ continue;
953
+ }
954
+ if ((_c = msg.change_types) == null ? void 0 : _c.includes("price")) {
955
+ const ids = msg.ids || (msg.id ? [msg.id] : []);
956
+ priceRefreshIds.push(...ids);
957
+ if (msg.body) {
958
+ const bodyId = msg.body.id;
959
+ if (bodyId)
960
+ bodyUpdates.set(bodyId, msg.body);
961
+ }
962
+ continue;
963
+ }
964
+ if (msg.body) {
965
+ const bodyId = msg.body.id || msg.id;
966
+ if (bodyId)
967
+ bodyUpdates.set(bodyId, msg.body);
968
+ continue;
969
+ }
970
+ if ((_d = msg.ids) == null ? void 0 : _d.length) {
971
+ sseRefreshIds.push(...msg.ids);
972
+ } else if (msg.id) {
973
+ sseRefreshIds.push(msg.id);
974
+ }
975
+ } else if (channelKey === "product_quotation") {
976
+ shouldClearPriceCache = true;
977
+ if ((_e = msg.relation_product_ids) == null ? void 0 : _e.length) {
978
+ sseRefreshIds.push(...msg.relation_product_ids);
979
+ }
980
+ } else if (["product_collection", "product_category"].includes(channelKey)) {
981
+ if ((_f = msg.relation_product_ids) == null ? void 0 : _f.length) {
982
+ sseRefreshIds.push(...msg.relation_product_ids);
983
+ }
984
+ }
985
+ }
986
+ const uniqueDeleteIds = [...new Set(deleteIds)];
987
+ const uniqueSSEIds = [...new Set(sseRefreshIds)];
988
+ const uniquePriceIds = [...new Set(priceRefreshIds)];
989
+ if (uniqueDeleteIds.length > 0) {
990
+ await this.removeProductsByIds(uniqueDeleteIds);
991
+ }
992
+ if (bodyUpdates.size > 0) {
993
+ await this.applyBodyUpdatesToStore(bodyUpdates);
994
+ }
995
+ if (uniqueSSEIds.length > 0) {
996
+ const freshProducts = await this.fetchProductsBySSE(uniqueSSEIds);
997
+ if (freshProducts.length > 0) {
998
+ await this.mergeProductsToStore(freshProducts);
999
+ }
1000
+ this.logInfo("processProductSyncMessages: SSE 增量更新完成123", {
1001
+ requestedCount: uniqueSSEIds.length,
1002
+ receivedCount: freshProducts.length
1003
+ });
1004
+ }
1005
+ const allChangedIds = [.../* @__PURE__ */ new Set([
1006
+ ...Array.from(bodyUpdates.keys()),
1007
+ ...uniqueSSEIds,
1008
+ ...uniquePriceIds
1009
+ ])];
1010
+ this.logInfo("processProductSyncMessages: 处理完成123", {
1011
+ deleteCount: uniqueDeleteIds.length,
1012
+ bodyUpdateCount: bodyUpdates.size,
1013
+ sseRefreshCount: uniqueSSEIds.length,
1014
+ priceRefreshCount: uniquePriceIds.length,
1015
+ allChangedIdsCount: allChangedIds.length,
1016
+ shouldClearPriceCache
1017
+ });
1018
+ const hasChanges = uniqueDeleteIds.length > 0 || allChangedIds.length > 0 || shouldClearPriceCache;
1019
+ if (!hasChanges) {
1020
+ this.logInfo("processProductSyncMessages: 没有变更,不触发 onProductsSyncCompleted");
1021
+ return;
1022
+ }
1023
+ if (shouldClearPriceCache) {
1024
+ this.clearPriceCache();
1025
+ }
1026
+ await this.core.effects.emit(import_types.ProductsHooks.onProductsSyncCompleted, {
1027
+ changedIds: allChangedIds
1028
+ });
1029
+ }
1030
+ /**
1031
+ * 通过 SSE 按 ids 增量拉取商品数据
1032
+ * 请求 GET /shop/core/stream?type=product&ids={ids}
1033
+ */
1034
+ async fetchProductsBySSE(ids) {
1035
+ if (!this.productDataSource) {
1036
+ this.logWarning("fetchProductsBySSE: ProductDataSource 不可用");
1037
+ return [];
1038
+ }
1039
+ this.logInfo("fetchProductsBySSE: 开始", { ids, count: ids.length });
1040
+ const t0 = performance.now();
1041
+ try {
1042
+ const productList = await this.productDataSource.run({
1043
+ sse: { query: { type: "product", ids } }
1044
+ });
1045
+ const list = productList || [];
1046
+ (0, import_product.perfMark)("fetchProductsBySSE", performance.now() - t0, { count: list.length });
1047
+ this.logInfo("fetchProductsBySSE: 成功", {
1048
+ requestedCount: ids.length,
1049
+ receivedCount: list.length,
1050
+ duration: `${Math.round(performance.now() - t0)}ms`
1051
+ });
1052
+ return list;
1053
+ } catch (error) {
1054
+ const errorMessage = error instanceof Error ? error.message : String(error);
1055
+ this.logError("fetchProductsBySSE: 失败", { ids, error: errorMessage });
1056
+ return [];
1057
+ }
1058
+ }
1059
+ /**
1060
+ * 将 body 完整数据直接覆盖到本地 store(不调用报价单接口)
1061
+ * 已存在的 → 直接替换;不存在的 → 追加
1062
+ * 同时更新 Map 缓存、IndexDB,触发 onProductsChanged
1063
+ * 价格缓存由 processProductSyncMessages 末尾统一清除
1064
+ */
1065
+ async applyBodyUpdatesToStore(bodyUpdates) {
1066
+ this.logInfo("applyBodyUpdatesToStore: 开始", { count: bodyUpdates.size });
1067
+ let updatedCount = 0;
1068
+ let newCount = 0;
1069
+ const appliedIds = /* @__PURE__ */ new Set();
1070
+ this.store.list = this.store.list.map((p) => {
1071
+ if (bodyUpdates.has(p.id)) {
1072
+ updatedCount++;
1073
+ appliedIds.add(p.id);
1074
+ return bodyUpdates.get(p.id);
1075
+ }
1076
+ return p;
1077
+ });
1078
+ for (const [id, body] of bodyUpdates) {
1079
+ if (!appliedIds.has(id)) {
1080
+ this.store.list.push(body);
1081
+ newCount++;
1082
+ }
1083
+ }
1084
+ this.syncProductsMap();
1085
+ await this.saveProductsToIndexDB(this.store.list);
1086
+ this.core.effects.emit(import_types.ProductsHooks.onProductsChanged, this.store.list);
1087
+ this.logInfo("applyBodyUpdatesToStore: 完成", {
1088
+ updatedCount,
1089
+ newCount,
1090
+ totalCount: this.store.list.length
1091
+ });
1092
+ }
1093
+ /**
1094
+ * 将增量拉取的商品合并到 store
1095
+ * 已存在的 → 替换;新的 → 追加
1096
+ * 同时更新 store.map、IndexDB,触发 onProductsChanged
1097
+ */
1098
+ async mergeProductsToStore(freshProducts) {
1099
+ const freshMap = /* @__PURE__ */ new Map();
1100
+ for (const p of freshProducts) {
1101
+ freshMap.set(p.id, p);
1102
+ }
1103
+ const updatedList = this.store.list.map((p) => {
1104
+ if (freshMap.has(p.id)) {
1105
+ const fresh = freshMap.get(p.id);
1106
+ freshMap.delete(p.id);
1107
+ return fresh;
1108
+ }
1109
+ return p;
1110
+ });
1111
+ const newCount = freshMap.size;
1112
+ for (const p of freshMap.values()) {
1113
+ updatedList.push(p);
1114
+ }
1115
+ const updatedCount = freshProducts.length - newCount;
1116
+ this.store.list = updatedList;
1117
+ this.syncProductsMap();
1118
+ await this.saveProductsToIndexDB(this.store.list);
1119
+ this.logInfo("mergeProductsToStore: 合并完成", {
1120
+ updatedCount,
1121
+ newCount,
1122
+ totalCount: this.store.list.length
1123
+ });
1124
+ this.core.effects.emit(import_types.ProductsHooks.onProductsChanged, this.store.list);
1125
+ }
1126
+ /**
1127
+ * 静默全量刷新:后台重新拉取全量 SSE 数据并更新本地
1128
+ * 拿到完整数据后一次性替换 store,清除价格缓存,触发 onProductsSyncCompleted
1129
+ * @returns 刷新后的商品列表
1130
+ */
1131
+ async silentRefresh() {
1132
+ const t0 = performance.now();
1133
+ this.logInfo("silentRefresh 开始");
1134
+ try {
1135
+ const products = await this.loadProductsByServer();
1136
+ if (products && products.length > 0) {
1137
+ this.store.list = products;
1138
+ this.syncProductsMap();
1139
+ this.clearPriceCache();
1140
+ this.core.effects.emit(import_types.ProductsHooks.onProductsChanged, this.store.list);
1141
+ await this.core.effects.emit(import_types.ProductsHooks.onProductsSyncCompleted, null);
1142
+ this.logInfo("silentRefresh 完成", {
1143
+ productCount: products.length,
1144
+ duration: `${Math.round(performance.now() - t0)}ms`
1145
+ });
1146
+ } else {
1147
+ this.logWarning("silentRefresh: 服务器未返回数据");
1148
+ }
1149
+ (0, import_product.perfMark)("silentRefresh", performance.now() - t0, { count: this.store.list.length });
1150
+ return this.store.list;
1151
+ } catch (error) {
1152
+ const errorMessage = error instanceof Error ? error.message : String(error);
1153
+ this.logError("silentRefresh 失败", {
1154
+ duration: `${Math.round(performance.now() - t0)}ms`,
1155
+ error: errorMessage
1156
+ });
1157
+ return this.store.list;
1158
+ }
1159
+ }
1160
+ /**
1161
+ * 销毁同步资源(取消 pubsub 订阅、清除定时器)
1162
+ */
1163
+ destroyProductSync() {
1164
+ var _a;
1165
+ if (this.syncTimer) {
1166
+ clearTimeout(this.syncTimer);
1167
+ this.syncTimer = void 0;
1168
+ }
1169
+ if ((_a = this.productDataSource) == null ? void 0 : _a.destroy) {
1170
+ this.productDataSource.destroy();
436
1171
  }
1172
+ this.pendingSyncMessages = [];
1173
+ this.logInfo("destroyProductSync: 同步资源已销毁");
437
1174
  }
438
1175
  /**
439
1176
  * 获取模块的路由定义