@pisell/pisellos 2.2.80 → 2.2.82

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.
@@ -23,11 +23,11 @@ __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");
26
27
  var import_plugins = require("../../../plugins");
27
28
  var import_types = require("./types");
28
29
  var import_product = require("../../utils/product");
29
30
  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);
@@ -44,7 +44,6 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
44
44
  this.formatters = [];
45
45
  // 是否已注册内置价格格式化器
46
46
  this.isPriceFormatterRegistered = false;
47
- this.pendingSyncMessages = [];
48
47
  }
49
48
  async initialize(core, options) {
50
49
  var _a;
@@ -72,8 +71,6 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
72
71
  }
73
72
  }
74
73
  this.registerBuiltinPriceFormatter();
75
- this.initProductDataSource();
76
- this.setupProductSync();
77
74
  this.logInfo("模块初始化完成", {
78
75
  hasDbManager: !!this.dbManager,
79
76
  hasLogger: !!this.logger,
@@ -189,32 +186,29 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
189
186
  * @returns 应用了价格的商品列表
190
187
  */
191
188
  async getProductsWithPrice(schedule_date, extraContext) {
192
- const t0 = performance.now();
193
189
  const cacheKey = schedule_date;
194
190
  if (this.productsPriceCache.has(cacheKey)) {
191
+ console.log(`[ProductsModule] 💰 商品价格缓存命中: ${cacheKey}`);
195
192
  const cachedProducts = this.productsPriceCache.get(cacheKey);
196
- (0, import_product.perfMark)("getProductsWithPrice(cacheHit)", performance.now() - t0, {
197
- cacheKey,
198
- count: cachedProducts.length
199
- });
200
193
  this.logInfo("商品价格缓存命中", {
201
194
  cacheKey,
202
195
  productCount: cachedProducts.length
203
196
  });
204
197
  return cachedProducts;
205
198
  }
199
+ console.log(`[ProductsModule] 🌐 获取商品并应用价格: ${cacheKey}`);
206
200
  this.logInfo("商品价格缓存未命中,准备获取", { cacheKey });
201
+ const startTime = Date.now();
207
202
  const result = await this.prepareProductsWithPrice(schedule_date, extraContext);
203
+ const duration = Date.now() - startTime;
208
204
  this.productsPriceCache.set(cacheKey, result);
205
+ console.log(`[ProductsModule] ✅ 商品价格已缓存: ${cacheKey}, 共 ${result.length} 个商品`);
209
206
  this.logInfo("商品价格已缓存", {
210
207
  cacheKey,
211
- productCount: result.length
208
+ productCount: result.length,
209
+ duration: `${duration}ms`
212
210
  });
213
211
  this.cleanExpiredPriceCache();
214
- (0, import_product.perfMark)("getProductsWithPrice(cacheMiss)", performance.now() - t0, {
215
- cacheKey,
216
- count: result.length
217
- });
218
212
  return result;
219
213
  }
220
214
  /**
@@ -225,35 +219,27 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
225
219
  * @private
226
220
  */
227
221
  async prepareProductsWithPrice(schedule_date, extraContext) {
228
- const tTotal = performance.now();
229
222
  this.logInfo("prepareProductsWithPrice 开始处理", { schedule_date });
230
223
  try {
231
- const allProducts = this.getProductsRef();
224
+ const allProducts = await this.getProducts();
232
225
  this.logInfo("获取到商品列表", { productCount: allProducts.length });
233
- const tIds = performance.now();
234
- const ids = new Array(allProducts.length);
235
- for (let i = 0; i < allProducts.length; i++) {
236
- ids[i] = allProducts[i].id;
237
- }
238
- (0, import_product.perfMark)("prepareProducts.extractIds", performance.now() - tIds, { count: ids.length });
239
- const tPrice = performance.now();
226
+ console.log(`[ProductsModule] 🌐 开始获取商品报价单价格`);
240
227
  const priceData = await this.loadProductsPrice({
241
- ids,
228
+ ids: allProducts.map((product) => product.id).sort((a, b) => a - b),
242
229
  schedule_date
243
230
  });
244
- (0, import_product.perfMark)("prepareProducts.loadPrice", performance.now() - tPrice, { count: ids.length });
231
+ console.log(`[ProductsModule] 🌐 获取商品报价单价格成功`, priceData);
245
232
  this.logInfo("获取商品报价单价格成功", { priceDataCount: (priceData == null ? void 0 : priceData.length) ?? 0 });
246
233
  const context = {
247
234
  schedule_date,
248
235
  priceData,
249
236
  ...extraContext
237
+ // 合并 Server 层传入的额外数据(如 scheduleList)
250
238
  };
251
- const tFormat = performance.now();
239
+ console.log(`[ProductsModule] 🌐 通过格式化器流程处理商品(包括价格应用、字段扩展等)`);
240
+ this.logInfo("开始通过格式化器流程处理商品", { formatterCount: this.formatters.length });
252
241
  const processedProducts = await this.applyFormatters(allProducts, context);
253
- (0, import_product.perfMark)("prepareProducts.applyFormatters", performance.now() - tFormat, {
254
- count: allProducts.length,
255
- formatterCount: this.formatters.length
256
- });
242
+ console.log(`[ProductsModule] 🌐 通过格式化器流程处理商品(包括价格应用、字段扩展等)成功`, processedProducts);
257
243
  this.logInfo("prepareProductsWithPrice 处理完成", {
258
244
  originalProductCount: allProducts.length,
259
245
  processedProductCount: processedProducts.length,
@@ -263,10 +249,6 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
263
249
  import_types.ProductsHooks.onProductsPriceApplied,
264
250
  processedProducts
265
251
  );
266
- (0, import_product.perfMark)("prepareProductsWithPrice", performance.now() - tTotal, {
267
- productCount: allProducts.length,
268
- formatterCount: this.formatters.length
269
- });
270
252
  return processedProducts;
271
253
  } catch (err) {
272
254
  const errorMessage = err instanceof Error ? err.message : String(err);
@@ -299,13 +281,8 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
299
281
  for (let i = 0; i < this.formatters.length; i++) {
300
282
  const formatter = this.formatters[i];
301
283
  try {
302
- const tF = performance.now();
284
+ console.log(`[ProductsModule] 📝 应用格式化器 ${i + 1}/${this.formatters.length}`);
303
285
  result = await formatter(result, context);
304
- (0, import_product.perfMark)(`applyFormatters[${i}]`, performance.now() - tF, {
305
- index: i,
306
- total: this.formatters.length,
307
- productCount: result.length
308
- });
309
286
  } catch (error) {
310
287
  const errorMessage = error instanceof Error ? error.message : String(error);
311
288
  console.error(`[ProductsModule] ❌ 格式化器 ${i + 1} 执行失败:`, error);
@@ -316,6 +293,7 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
316
293
  });
317
294
  }
318
295
  }
296
+ console.log(`[ProductsModule] ✅ 所有格式化器已应用,共 ${this.formatters.length} 个`);
319
297
  this.logInfo("所有格式化器已应用", {
320
298
  formatterCount: this.formatters.length,
321
299
  resultProductCount: result.length
@@ -409,45 +387,10 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
409
387
  console.log("[ProductsModule] 🗑️ 商品价格缓存已清空");
410
388
  }
411
389
  /**
412
- * 通过 ProductDataSource SSE 加载完整商品列表
413
- */
414
- async loadProductsByServer() {
415
- if (!this.productDataSource) {
416
- this.logWarning("loadProductsByServer: ProductDataSource 不可用");
417
- return [];
418
- }
419
- this.logInfo("开始通过 DataSource SSE 加载商品列表");
420
- const t0 = performance.now();
421
- try {
422
- const tSSE = performance.now();
423
- const productList = await this.productDataSource.run({ sse: {} });
424
- const list = productList || [];
425
- (0, import_product.perfMark)("loadProductsByServer.SSE", performance.now() - tSSE, { count: list.length });
426
- this.logInfo("通过 DataSource SSE 加载商品列表成功", {
427
- productCount: list.length,
428
- duration: `${Math.round(performance.now() - t0)}ms`
429
- });
430
- await this.saveProductsToIndexDB(list);
431
- await this.core.effects.emit(import_types.ProductsHooks.onProductsLoaded, list);
432
- (0, import_product.perfMark)("loadProductsByServer", performance.now() - t0, { count: list.length });
433
- return list;
434
- } catch (error) {
435
- const duration = Math.round(performance.now() - t0);
436
- const errorMessage = error instanceof Error ? error.message : String(error);
437
- console.error("[Products] 加载商品数据失败:", error);
438
- this.logError("通过 DataSource SSE 加载商品列表失败", {
439
- duration: `${duration}ms`,
440
- error: errorMessage
441
- });
442
- return [];
443
- }
444
- }
445
- /**
446
- * 纯请求方法:通过 HTTP 接口获取商品列表(无副作用,不触发事件、不写 IndexDB)
390
+ * 加载完整商品列表通过接口(包含所有详细数据)
447
391
  * @param params 查询参数
448
- * @returns 商品列表
449
392
  */
450
- async fetchProductsByHttp(params) {
393
+ async loadProductsByServer(params) {
451
394
  var _a, _b;
452
395
  const {
453
396
  category_ids = [],
@@ -456,7 +399,7 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
456
399
  customer_id,
457
400
  cacheId
458
401
  } = params || {};
459
- this.logInfo("fetchProductsByHttp: 开始请求", {
402
+ this.logInfo("开始从服务器加载商品列表", {
460
403
  categoryIdsCount: category_ids.length,
461
404
  productIdsCount: product_ids.length,
462
405
  collectionCount: Array.isArray(collection) ? collection.length : collection ? 1 : 0,
@@ -477,12 +420,16 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
477
420
  "event_item"
478
421
  ],
479
422
  force_ignore_cache_flag: 1,
423
+ // 获取所有详细信息
480
424
  with: [
481
425
  "category",
482
426
  "collection",
483
427
  "resourceRelation",
428
+ // 套餐
484
429
  "bundleGroup.bundleItem",
430
+ // 单规格
485
431
  "optionGroup.optionItem",
432
+ // 组合规格
486
433
  "variantGroup.variantItem"
487
434
  ],
488
435
  open_deposit: 1,
@@ -502,16 +449,21 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
502
449
  );
503
450
  const productList = ((_b = productsData == null ? void 0 : productsData.data) == null ? void 0 : _b.list) || [];
504
451
  const duration = Date.now() - startTime;
505
- this.logInfo("fetchProductsByHttp: 请求成功", {
452
+ this.logInfo("从服务器加载商品列表成功", {
506
453
  productCount: productList.length,
507
454
  duration: `${duration}ms`
508
455
  });
509
- return productList;
456
+ await this.saveProductsToIndexDB(productList);
457
+ await this.core.effects.emit(
458
+ import_types.ProductsHooks.onProductsLoaded,
459
+ productsData.data.list
460
+ );
461
+ return productsData.data.list;
510
462
  } catch (error) {
511
463
  const duration = Date.now() - startTime;
512
464
  const errorMessage = error instanceof Error ? error.message : String(error);
513
- console.error("[Products] fetchProductsByHttp 失败:", error);
514
- this.logError("fetchProductsByHttp: 请求失败", {
465
+ console.error("[Products] 加载商品数据失败:", error);
466
+ this.logError("从服务器加载商品列表失败", {
515
467
  duration: `${duration}ms`,
516
468
  error: errorMessage
517
469
  });
@@ -519,33 +471,10 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
519
471
  }
520
472
  }
521
473
  /**
522
- * 加载完整商品列表通过接口(包含所有详细数据)
523
- * 包含副作用:保存到 IndexDB + 触发 onProductsLoaded 事件
524
- * @param params 查询参数
525
- */
526
- async loadProductsByServerHttp(params) {
527
- const productList = await this.fetchProductsByHttp(params);
528
- if (productList.length > 0) {
529
- await this.saveProductsToIndexDB(productList);
530
- await this.core.effects.emit(import_types.ProductsHooks.onProductsLoaded, productList);
531
- }
532
- return productList;
533
- }
534
- /**
535
- * 获取商品列表(深拷贝,供外部安全使用)
474
+ * 获取商品列表(从缓存)
536
475
  */
537
476
  async getProducts() {
538
- const t0 = performance.now();
539
- const result = structuredClone(this.store.list);
540
- (0, import_product.perfMark)("ProductsModule.getProducts(structuredClone)", performance.now() - t0, { count: result.length });
541
- return result;
542
- }
543
- /**
544
- * 内部获取商品列表的直接引用(无拷贝)
545
- * 仅供内部 formatter 流程使用,因为 formatter 会创建新对象
546
- */
547
- getProductsRef() {
548
- return this.store.list;
477
+ return (0, import_lodash_es.cloneDeep)(this.store.list);
549
478
  }
550
479
  /**
551
480
  * 根据ID获取单个商品(从内存缓存)
@@ -553,79 +482,7 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
553
482
  */
554
483
  async getProductById(id) {
555
484
  const product = this.store.map.get(id);
556
- return product ? structuredClone(product) : void 0;
557
- }
558
- /**
559
- * 根据 ID 列表删除商品(用于 pubsub 同步删除场景)
560
- * 同时更新 store.list、store.map、IndexDB 和价格缓存
561
- */
562
- async removeProductsByIds(ids) {
563
- const idSet = new Set(ids);
564
- this.logInfo("removeProductsByIds", { ids, count: ids.length });
565
- this.store.list = this.store.list.filter((p) => !idSet.has(p.id));
566
- for (const id of ids) {
567
- this.store.map.delete(id);
568
- }
569
- if (this.dbManager) {
570
- try {
571
- for (const id of ids) {
572
- await this.dbManager.delete(INDEXDB_STORE_NAME, id);
573
- }
574
- } catch (error) {
575
- const errorMessage = error instanceof Error ? error.message : String(error);
576
- this.logError("removeProductsByIds: IndexDB 删除失败", { ids, error: errorMessage });
577
- }
578
- }
579
- for (const [dateKey, cachedProducts] of this.productsPriceCache.entries()) {
580
- this.productsPriceCache.set(
581
- dateKey,
582
- cachedProducts.filter((p) => !idSet.has(p.id))
583
- );
584
- }
585
- this.core.effects.emit(import_types.ProductsHooks.onProductsChanged, this.store.list);
586
- this.logInfo("removeProductsByIds 完成", { remaining: this.store.list.length });
587
- }
588
- /**
589
- * 重新从服务器加载全量商品列表并更新本地 store
590
- * 用于 pubsub 同步 create / update / batch_update 场景
591
- */
592
- async refreshProducts() {
593
- const tTotal = performance.now();
594
- this.logInfo("refreshProducts 开始");
595
- const products = await this.loadProductsByServer();
596
- if (products && products.length > 0) {
597
- this.store.list = products;
598
- this.syncProductsMap();
599
- this.core.effects.emit(import_types.ProductsHooks.onProductsChanged, this.store.list);
600
- this.logInfo("refreshProducts 完成", { productCount: products.length });
601
- } else {
602
- this.logWarning("refreshProducts: 服务器未返回数据");
603
- }
604
- (0, import_product.perfMark)("refreshProducts", performance.now() - tTotal, { count: this.store.list.length });
605
- return this.store.list;
606
- }
607
- /**
608
- * 局部更新指定商品的报价单价格
609
- * 遍历所有已缓存的日期,为目标商品重新获取价格并覆盖到缓存中
610
- */
611
- async updateProductPriceByIds(ids) {
612
- this.logInfo("updateProductPriceByIds", { ids });
613
- for (const [dateKey, cachedProducts] of this.productsPriceCache.entries()) {
614
- try {
615
- const priceData = await this.loadProductsPrice({
616
- ids,
617
- schedule_date: dateKey
618
- });
619
- if (priceData && priceData.length > 0) {
620
- const updatedProducts = (0, import_product.applyPriceDataToProducts)(cachedProducts, priceData);
621
- this.productsPriceCache.set(dateKey, updatedProducts);
622
- this.logInfo("updateProductPriceByIds: 缓存已更新", { dateKey, priceDataCount: priceData.length });
623
- }
624
- } catch (error) {
625
- const errorMessage = error instanceof Error ? error.message : String(error);
626
- this.logError("updateProductPriceByIds: 失败", { dateKey, ids, error: errorMessage });
627
- }
628
- }
485
+ return product ? (0, import_lodash_es.cloneDeep)(product) : void 0;
629
486
  }
630
487
  /**
631
488
  * 清空缓存
@@ -661,9 +518,7 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
661
518
  return [];
662
519
  }
663
520
  try {
664
- const t0 = performance.now();
665
521
  const products = await this.dbManager.getAll(INDEXDB_STORE_NAME);
666
- (0, import_product.perfMark)("loadProductsFromIndexDB", performance.now() - t0, { count: (products == null ? void 0 : products.length) ?? 0 });
667
522
  this.logInfo("从 IndexDB 加载商品数据", {
668
523
  productCount: (products == null ? void 0 : products.length) ?? 0
669
524
  });
@@ -686,10 +541,11 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
686
541
  }
687
542
  this.logInfo("开始保存商品数据到 IndexDB", { productCount: products.length });
688
543
  try {
689
- const t0 = performance.now();
690
544
  await this.dbManager.clear(INDEXDB_STORE_NAME);
691
- await this.dbManager.bulkAdd(INDEXDB_STORE_NAME, products);
692
- (0, import_product.perfMark)("saveProductsToIndexDB", performance.now() - t0, { count: products.length });
545
+ const savePromises = products.map(
546
+ (product) => this.dbManager.add(INDEXDB_STORE_NAME, product)
547
+ );
548
+ await Promise.all(savePromises);
693
549
  console.log(
694
550
  `[Products] 已将 ${products.length} 个商品平铺保存到 IndexDB`
695
551
  );
@@ -709,12 +565,11 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
709
565
  * @private
710
566
  */
711
567
  syncProductsMap() {
712
- const t0 = performance.now();
713
568
  this.store.map.clear();
714
569
  for (const product of this.store.list) {
715
570
  this.store.map.set(product.id, product);
716
571
  }
717
- (0, import_product.perfMark)("syncProductsMap", performance.now() - t0, { count: this.store.map.size });
572
+ console.log(`[Products] Map 缓存已同步,共 ${this.store.map.size} 个商品`);
718
573
  }
719
574
  /**
720
575
  * 预加载模块数据(统一接口)
@@ -722,29 +577,22 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
722
577
  */
723
578
  async preload() {
724
579
  console.log("[Products] 开始预加载数据...");
725
- const tTotal = performance.now();
580
+ const startTime = Date.now();
726
581
  this.logInfo("开始预加载数据");
727
582
  try {
728
- const tIndexDB = performance.now();
729
583
  const cachedData = await this.loadProductsFromIndexDB();
730
- (0, import_product.perfMark)("preload.loadFromIndexDB", performance.now() - tIndexDB, { count: (cachedData == null ? void 0 : cachedData.length) ?? 0 });
731
584
  if (cachedData && cachedData.length > 0) {
732
585
  console.log(`[Products] 从 IndexDB 加载了 ${cachedData.length} 个商品`);
733
- this.store.list = cachedData;
734
- const tSync = performance.now();
586
+ this.store.list = (0, import_lodash_es.cloneDeep)(cachedData);
735
587
  this.syncProductsMap();
736
- (0, import_product.perfMark)("preload.syncProductsMap", performance.now() - tSync, { count: cachedData.length });
737
588
  this.core.effects.emit(
738
589
  import_types.ProductsHooks.onProductsChanged,
739
590
  this.store.list
740
591
  );
741
- (0, import_product.perfMark)("preload(IndexDB)", performance.now() - tTotal, {
742
- count: cachedData.length,
743
- source: "IndexDB"
744
- });
592
+ const duration = Date.now() - startTime;
745
593
  this.logInfo("预加载完成(从 IndexDB)", {
746
594
  productCount: cachedData.length,
747
- duration: `${Math.round(performance.now() - tTotal)}ms`,
595
+ duration: `${duration}ms`,
748
596
  source: "IndexDB"
749
597
  });
750
598
  return;
@@ -756,416 +604,24 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
756
604
  console.warn("[Products] 从 IndexDB 加载数据失败:", error);
757
605
  this.logWarning("从 IndexDB 加载数据失败,准备从服务器加载", { error: errorMessage });
758
606
  }
759
- const tServer = performance.now();
760
607
  const products = await this.loadProductsByServer();
761
- (0, import_product.perfMark)("preload.loadFromServer", performance.now() - tServer, { count: (products == null ? void 0 : products.length) ?? 0 });
762
608
  if (products && products.length > 0) {
763
- this.store.list = products;
764
- const tSync = performance.now();
609
+ await this.saveProductsToIndexDB(products);
610
+ this.store.list = (0, import_lodash_es.cloneDeep)(products);
765
611
  this.syncProductsMap();
766
- (0, import_product.perfMark)("preload.syncProductsMap", performance.now() - tSync, { count: products.length });
767
612
  this.core.effects.emit(import_types.ProductsHooks.onProductsChanged, this.store.list);
768
- (0, import_product.perfMark)("preload(Server)", performance.now() - tTotal, {
769
- count: products.length,
770
- source: "Server"
771
- });
613
+ const duration = Date.now() - startTime;
772
614
  this.logInfo("预加载完成(从服务器)", {
773
615
  productCount: products.length,
774
- duration: `${Math.round(performance.now() - tTotal)}ms`,
616
+ duration: `${duration}ms`,
775
617
  source: "Server"
776
618
  });
777
619
  } else {
778
- (0, import_product.perfMark)("preload(empty)", performance.now() - tTotal, { source: "empty" });
620
+ const duration = Date.now() - startTime;
779
621
  this.logWarning("预加载完成但未获取到数据", {
780
- duration: `${Math.round(performance.now() - tTotal)}ms`
781
- });
782
- }
783
- }
784
- // =============================================
785
- // 商品数据同步(pubsub)
786
- // =============================================
787
- /**
788
- * 初始化 ProductDataSource 实例
789
- * 与 pubsub 订阅和数据获取分开,仅负责创建实例
790
- */
791
- initProductDataSource() {
792
- var _a, _b;
793
- const ProductDataSourceClass = (_b = (_a = this.core.serverOptions) == null ? void 0 : _a.All_DATA_SOURCES) == null ? void 0 : _b.ProductDataSource;
794
- if (!ProductDataSourceClass) {
795
- this.logWarning("initProductDataSource: ProductDataSource 不可用");
796
- return;
797
- }
798
- this.productDataSource = new ProductDataSourceClass();
799
- this.logInfo("ProductDataSource 实例已创建");
800
- }
801
- /**
802
- * 初始化 pubsub 订阅,监听管理后台商品变更
803
- * 仅负责订阅 product / product_quotation 频道,消息通过防抖合并后批量处理
804
- * 数据获取由 loadProductsByServer 单独负责
805
- */
806
- setupProductSync() {
807
- if (!this.productDataSource) {
808
- this.logWarning("setupProductSync: ProductDataSource 不可用,跳过同步初始化");
809
- return;
810
- }
811
- const createHandler = (channelKey) => (message) => {
812
- const data = (message == null ? void 0 : message.data) || message;
813
- if (!data)
814
- return;
815
- console.log(`[ProductsModule] 收到同步消息 [${channelKey}]:`, data);
816
- this.logInfo("收到同步消息", {
817
- channelKey,
818
- action: data.action,
819
- id: data.id,
820
- action_filed: data.action_filed
821
- });
822
- this.pendingSyncMessages.push({ ...data, _channelKey: channelKey });
823
- if (this.syncTimer) {
824
- clearTimeout(this.syncTimer);
825
- }
826
- this.syncTimer = setTimeout(() => {
827
- this.processProductSyncMessages();
828
- }, PRODUCT_SYNC_DEBOUNCE_MS);
829
- };
830
- this.productDataSource.run({
831
- pubsub: {
832
- callback: (res) => {
833
- console.log("sse_products_callback", res);
834
- this.logInfo("sse_products_callback: 收到同步消息", {
835
- data: res == null ? void 0 : res.data
836
- });
837
- if (!(res == null ? void 0 : res.data))
838
- return;
839
- const data = res.data;
840
- const channelKey = data.module || "product";
841
- createHandler(channelKey)(data);
842
- }
843
- }
844
- }).catch((err) => {
845
- this.logError("setupProductSync: DataSource run 出错", {
846
- error: err instanceof Error ? err.message : String(err)
847
- });
848
- });
849
- this.logInfo("setupProductSync: pubsub 订阅已建立");
850
- }
851
- /**
852
- * 处理防抖后的同步消息批次
853
- *
854
- * product 模块:
855
- * - operation === 'delete' → 本地删除
856
- * - 有 body(无 price change_types) → body 完整数据直接覆盖本地
857
- * - change_types 包含 price → SSE 增量拉取 + 刷新报价单价格缓存
858
- * - change_types 仅 stock → 跳过(暂不响应)
859
- *
860
- * product_collection / product_category / product_quotation:
861
- * - 按 relation_product_ids SSE 拉取受影响商品
862
- * - product_quotation 额外刷新报价单价格缓存
863
- *
864
- * 处理完成后 emit onProductsSyncCompleted 通知 Server 层
865
- */
866
- async processProductSyncMessages() {
867
- var _a, _b, _c, _d, _e;
868
- const messages = [...this.pendingSyncMessages];
869
- this.pendingSyncMessages = [];
870
- if (messages.length === 0)
871
- return;
872
- this.logInfo("processProductSyncMessages: 开始处理", { count: messages.length });
873
- const deleteIds = [];
874
- const bodyUpdates = /* @__PURE__ */ new Map();
875
- const sseRefreshIds = [];
876
- const priceRefreshIds = [];
877
- for (const msg of messages) {
878
- const channelKey = msg._channelKey || msg.module || "product";
879
- if (channelKey === "product") {
880
- if (msg.operation === "delete" || msg.action === "delete") {
881
- if ((_a = msg.ids) == null ? void 0 : _a.length)
882
- deleteIds.push(...msg.ids);
883
- else if (msg.id)
884
- deleteIds.push(msg.id);
885
- continue;
886
- }
887
- if (((_b = msg.change_types) == null ? void 0 : _b.length) && msg.change_types.every((t) => t === "stock")) {
888
- this.logInfo("跳过仅库存变更", { ids: msg.ids });
889
- continue;
890
- }
891
- if ((_c = msg.change_types) == null ? void 0 : _c.includes("price")) {
892
- const ids = msg.ids || (msg.id ? [msg.id] : []);
893
- sseRefreshIds.push(...ids);
894
- priceRefreshIds.push(...ids);
895
- continue;
896
- }
897
- if (msg.body) {
898
- const bodyId = msg.body.id || msg.id;
899
- if (bodyId)
900
- bodyUpdates.set(bodyId, msg.body);
901
- continue;
902
- }
903
- if ((_d = msg.ids) == null ? void 0 : _d.length) {
904
- sseRefreshIds.push(...msg.ids);
905
- } else if (msg.id) {
906
- sseRefreshIds.push(msg.id);
907
- }
908
- } else if (["product_collection", "product_category", "product_quotation"].includes(channelKey)) {
909
- if ((_e = msg.relation_product_ids) == null ? void 0 : _e.length) {
910
- sseRefreshIds.push(...msg.relation_product_ids);
911
- if (channelKey === "product_quotation") {
912
- this.clearPriceCache();
913
- priceRefreshIds.push(...msg.relation_product_ids);
914
- }
915
- }
916
- }
917
- }
918
- const uniqueDeleteIds = [...new Set(deleteIds)];
919
- const uniqueSSEIds = [...new Set(sseRefreshIds)];
920
- const uniquePriceIds = [...new Set(priceRefreshIds)];
921
- if (uniqueDeleteIds.length > 0) {
922
- await this.removeProductsByIds(uniqueDeleteIds);
923
- }
924
- if (bodyUpdates.size > 0) {
925
- await this.applyBodyUpdatesToStore(bodyUpdates);
926
- }
927
- if (uniqueSSEIds.length > 0) {
928
- const freshProducts = await this.fetchProductsBySSE(uniqueSSEIds);
929
- if (freshProducts.length > 0) {
930
- await this.mergeProductsToStore(freshProducts);
931
- await this.updatePriceCacheForProducts(freshProducts);
932
- }
933
- this.logInfo("processProductSyncMessages: SSE 增量更新完成", {
934
- requestedCount: uniqueSSEIds.length,
935
- receivedCount: freshProducts.length
936
- });
937
- }
938
- const sseHandledSet = new Set(uniqueSSEIds);
939
- const remainingPriceIds = uniquePriceIds.filter((id) => !sseHandledSet.has(id));
940
- if (remainingPriceIds.length > 0) {
941
- await this.updateProductPriceByIds(remainingPriceIds);
942
- }
943
- this.logInfo("processProductSyncMessages: 处理完成", {
944
- deleteCount: uniqueDeleteIds.length,
945
- bodyUpdateCount: bodyUpdates.size,
946
- sseRefreshCount: uniqueSSEIds.length,
947
- priceRefreshCount: uniquePriceIds.length
948
- });
949
- await this.core.effects.emit(import_types.ProductsHooks.onProductsSyncCompleted, null);
950
- }
951
- /**
952
- * 通过 SSE 按 ids 增量拉取商品数据
953
- * 请求 GET /shop/core/stream?type=product&ids={ids}
954
- */
955
- async fetchProductsBySSE(ids) {
956
- if (!this.productDataSource) {
957
- this.logWarning("fetchProductsBySSE: ProductDataSource 不可用");
958
- return [];
959
- }
960
- this.logInfo("fetchProductsBySSE: 开始", { ids, count: ids.length });
961
- const t0 = performance.now();
962
- try {
963
- const productList = await this.productDataSource.run({
964
- sse: { query: { type: "product", ids } }
965
- });
966
- const list = productList || [];
967
- (0, import_product.perfMark)("fetchProductsBySSE", performance.now() - t0, { count: list.length });
968
- this.logInfo("fetchProductsBySSE: 成功", {
969
- requestedCount: ids.length,
970
- receivedCount: list.length,
971
- duration: `${Math.round(performance.now() - t0)}ms`
972
- });
973
- return list;
974
- } catch (error) {
975
- const errorMessage = error instanceof Error ? error.message : String(error);
976
- this.logError("fetchProductsBySSE: 失败", { ids, error: errorMessage });
977
- return [];
978
- }
979
- }
980
- /**
981
- * 将 body 完整数据直接覆盖到本地 store(不调用报价单接口)
982
- * 已存在的 → 直接替换;不存在的 → 追加
983
- * 同时更新 Map 缓存、IndexDB,清空价格缓存,触发 onProductsChanged
984
- */
985
- async applyBodyUpdatesToStore(bodyUpdates) {
986
- this.logInfo("applyBodyUpdatesToStore: 开始", { count: bodyUpdates.size });
987
- let updatedCount = 0;
988
- let newCount = 0;
989
- const appliedIds = /* @__PURE__ */ new Set();
990
- this.store.list = this.store.list.map((p) => {
991
- if (bodyUpdates.has(p.id)) {
992
- updatedCount++;
993
- appliedIds.add(p.id);
994
- return bodyUpdates.get(p.id);
995
- }
996
- return p;
997
- });
998
- for (const [id, body] of bodyUpdates) {
999
- if (!appliedIds.has(id)) {
1000
- this.store.list.push(body);
1001
- newCount++;
1002
- }
1003
- }
1004
- this.syncProductsMap();
1005
- await this.saveProductsToIndexDB(this.store.list);
1006
- await this.updatePriceCacheForProducts([...bodyUpdates.values()]);
1007
- this.core.effects.emit(import_types.ProductsHooks.onProductsChanged, this.store.list);
1008
- this.logInfo("applyBodyUpdatesToStore: 完成", {
1009
- updatedCount,
1010
- newCount,
1011
- totalCount: this.store.list.length
1012
- });
1013
- }
1014
- /**
1015
- * 将增量拉取的商品合并到 store
1016
- * 已存在的 → 替换;新的 → 追加
1017
- * 同时更新 store.map、IndexDB,触发 onProductsChanged
1018
- */
1019
- async mergeProductsToStore(freshProducts) {
1020
- const freshMap = /* @__PURE__ */ new Map();
1021
- for (const p of freshProducts) {
1022
- freshMap.set(p.id, p);
1023
- }
1024
- const updatedList = this.store.list.map((p) => {
1025
- if (freshMap.has(p.id)) {
1026
- const fresh = freshMap.get(p.id);
1027
- freshMap.delete(p.id);
1028
- return fresh;
1029
- }
1030
- return p;
1031
- });
1032
- const newCount = freshMap.size;
1033
- for (const p of freshMap.values()) {
1034
- updatedList.push(p);
1035
- }
1036
- const updatedCount = freshProducts.length - newCount;
1037
- this.store.list = updatedList;
1038
- this.syncProductsMap();
1039
- await this.saveProductsToIndexDB(this.store.list);
1040
- this.logInfo("mergeProductsToStore: 合并完成", {
1041
- updatedCount,
1042
- newCount,
1043
- totalCount: this.store.list.length
1044
- });
1045
- this.core.effects.emit(import_types.ProductsHooks.onProductsChanged, this.store.list);
1046
- }
1047
- /**
1048
- * 增量更新价格缓存中变更的商品
1049
- * 对每个已缓存的日期 key:替换/追加最新商品数据,重新拉取这些 ID 的价格并应用
1050
- */
1051
- async updatePriceCacheForProducts(freshProducts) {
1052
- if (this.productsPriceCache.size === 0)
1053
- return;
1054
- const freshIds = freshProducts.map((p) => p.id);
1055
- const freshMap = /* @__PURE__ */ new Map();
1056
- for (const p of freshProducts) {
1057
- freshMap.set(p.id, p);
1058
- }
1059
- this.logInfo("updatePriceCacheForProducts: 开始", {
1060
- freshIds,
1061
- cachedDateCount: this.productsPriceCache.size
1062
- });
1063
- for (const [dateKey, cachedProducts] of this.productsPriceCache.entries()) {
1064
- try {
1065
- const updatedList = cachedProducts.map((p) => {
1066
- const fresh = freshMap.get(p.id);
1067
- return fresh ? { ...fresh } : p;
1068
- });
1069
- const existingIds = new Set(cachedProducts.map((p) => p.id));
1070
- for (const p of freshProducts) {
1071
- if (!existingIds.has(p.id)) {
1072
- updatedList.push({ ...p });
1073
- }
1074
- }
1075
- const priceData = await this.loadProductsPrice({
1076
- ids: freshIds,
1077
- schedule_date: dateKey
1078
- });
1079
- const result = priceData && priceData.length > 0 ? (0, import_product.applyPriceDataToProducts)(updatedList, priceData) : updatedList;
1080
- this.productsPriceCache.set(dateKey, result);
1081
- this.logInfo("updatePriceCacheForProducts: 日期缓存已更新", {
1082
- dateKey,
1083
- updatedIds: freshIds
1084
- });
1085
- } catch (error) {
1086
- const errorMessage = error instanceof Error ? error.message : String(error);
1087
- this.logError("updatePriceCacheForProducts: 失败", { dateKey, error: errorMessage });
1088
- }
1089
- }
1090
- }
1091
- /**
1092
- * 全量重新拉取报价单价格并重建价格缓存
1093
- * 遍历当前已缓存的所有日期 key,对每个日期重新调用 loadProductsPrice
1094
- */
1095
- async refreshAllPriceCache() {
1096
- const allProducts = this.getProductsRef();
1097
- if (allProducts.length === 0)
1098
- return;
1099
- const ids = allProducts.map((p) => p.id);
1100
- const dateKeys = Array.from(this.productsPriceCache.keys());
1101
- this.clearPriceCache();
1102
- if (dateKeys.length === 0) {
1103
- this.logInfo("refreshAllPriceCache: 无已缓存日期,跳过");
1104
- return;
1105
- }
1106
- this.logInfo("refreshAllPriceCache: 开始重新拉取", { dateKeys, productCount: ids.length });
1107
- for (const dateKey of dateKeys) {
1108
- try {
1109
- const priceData = await this.loadProductsPrice({ ids, schedule_date: dateKey });
1110
- if (priceData && priceData.length > 0) {
1111
- const updatedProducts = (0, import_product.applyPriceDataToProducts)(allProducts, priceData);
1112
- this.productsPriceCache.set(dateKey, updatedProducts);
1113
- }
1114
- this.logInfo("refreshAllPriceCache: 日期缓存已更新", { dateKey });
1115
- } catch (error) {
1116
- const errorMessage = error instanceof Error ? error.message : String(error);
1117
- this.logError("refreshAllPriceCache: 失败", { dateKey, error: errorMessage });
1118
- }
1119
- }
1120
- }
1121
- /**
1122
- * 静默全量刷新:后台重新拉取全量 SSE 数据并更新本地
1123
- * 拿到完整数据后一次性替换 store,清除价格缓存,触发 onProductsSyncCompleted
1124
- * @returns 刷新后的商品列表
1125
- */
1126
- async silentRefresh() {
1127
- const t0 = performance.now();
1128
- this.logInfo("silentRefresh 开始");
1129
- try {
1130
- const products = await this.loadProductsByServer();
1131
- if (products && products.length > 0) {
1132
- this.store.list = products;
1133
- this.syncProductsMap();
1134
- this.clearPriceCache();
1135
- this.core.effects.emit(import_types.ProductsHooks.onProductsChanged, this.store.list);
1136
- await this.core.effects.emit(import_types.ProductsHooks.onProductsSyncCompleted, null);
1137
- this.logInfo("silentRefresh 完成", {
1138
- productCount: products.length,
1139
- duration: `${Math.round(performance.now() - t0)}ms`
1140
- });
1141
- } else {
1142
- this.logWarning("silentRefresh: 服务器未返回数据");
1143
- }
1144
- (0, import_product.perfMark)("silentRefresh", performance.now() - t0, { count: this.store.list.length });
1145
- return this.store.list;
1146
- } catch (error) {
1147
- const errorMessage = error instanceof Error ? error.message : String(error);
1148
- this.logError("silentRefresh 失败", {
1149
- duration: `${Math.round(performance.now() - t0)}ms`,
1150
- error: errorMessage
622
+ duration: `${duration}ms`
1151
623
  });
1152
- return this.store.list;
1153
- }
1154
- }
1155
- /**
1156
- * 销毁同步资源(取消 pubsub 订阅、清除定时器)
1157
- */
1158
- destroyProductSync() {
1159
- var _a;
1160
- if (this.syncTimer) {
1161
- clearTimeout(this.syncTimer);
1162
- this.syncTimer = void 0;
1163
- }
1164
- if ((_a = this.productDataSource) == null ? void 0 : _a.destroy) {
1165
- this.productDataSource.destroy();
1166
624
  }
1167
- this.pendingSyncMessages = [];
1168
- this.logInfo("destroyProductSync: 同步资源已销毁");
1169
625
  }
1170
626
  /**
1171
627
  * 获取模块的路由定义