@pisell/pisellos 2.2.78 → 2.2.80
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.d.ts +2 -2
- package/dist/modules/ProductList/index.d.ts +3 -0
- package/dist/modules/ProductList/index.js +9 -7
- package/dist/plugins/request.d.ts +2 -0
- package/dist/server/index.d.ts +27 -2
- package/dist/server/index.js +499 -295
- package/dist/server/modules/products/index.d.ts +101 -3
- package/dist/server/modules/products/index.js +1381 -242
- package/dist/server/modules/products/types.d.ts +24 -1
- package/dist/server/modules/products/types.js +3 -0
- package/dist/server/utils/product.d.ts +1 -0
- package/dist/server/utils/product.js +35 -25
- package/dist/solution/BookingTicket/index.d.ts +9 -1
- package/dist/solution/BookingTicket/index.js +41 -28
- package/dist/types/index.d.ts +3 -0
- package/lib/core/index.d.ts +2 -2
- package/lib/modules/ProductList/index.d.ts +3 -0
- package/lib/modules/ProductList/index.js +2 -2
- package/lib/plugins/request.d.ts +2 -0
- package/lib/server/index.d.ts +27 -2
- package/lib/server/index.js +172 -95
- package/lib/server/modules/products/index.d.ts +101 -3
- package/lib/server/modules/products/index.js +596 -52
- package/lib/server/modules/products/types.d.ts +24 -1
- package/lib/server/modules/products/types.js +1 -0
- package/lib/server/utils/product.d.ts +1 -0
- package/lib/server/utils/product.js +27 -11
- package/lib/solution/BookingTicket/index.d.ts +9 -1
- package/lib/solution/BookingTicket/index.js +10 -2
- package/lib/types/index.d.ts +3 -0
- package/package.json +1 -1
|
@@ -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");
|
|
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);
|
|
@@ -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;
|
|
@@ -71,6 +72,8 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
71
72
|
}
|
|
72
73
|
}
|
|
73
74
|
this.registerBuiltinPriceFormatter();
|
|
75
|
+
this.initProductDataSource();
|
|
76
|
+
this.setupProductSync();
|
|
74
77
|
this.logInfo("模块初始化完成", {
|
|
75
78
|
hasDbManager: !!this.dbManager,
|
|
76
79
|
hasLogger: !!this.logger,
|
|
@@ -186,29 +189,32 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
186
189
|
* @returns 应用了价格的商品列表
|
|
187
190
|
*/
|
|
188
191
|
async getProductsWithPrice(schedule_date, extraContext) {
|
|
192
|
+
const t0 = performance.now();
|
|
189
193
|
const cacheKey = schedule_date;
|
|
190
194
|
if (this.productsPriceCache.has(cacheKey)) {
|
|
191
|
-
console.log(`[ProductsModule] 💰 商品价格缓存命中: ${cacheKey}`);
|
|
192
195
|
const cachedProducts = this.productsPriceCache.get(cacheKey);
|
|
196
|
+
(0, import_product.perfMark)("getProductsWithPrice(cacheHit)", performance.now() - t0, {
|
|
197
|
+
cacheKey,
|
|
198
|
+
count: cachedProducts.length
|
|
199
|
+
});
|
|
193
200
|
this.logInfo("商品价格缓存命中", {
|
|
194
201
|
cacheKey,
|
|
195
202
|
productCount: cachedProducts.length
|
|
196
203
|
});
|
|
197
204
|
return cachedProducts;
|
|
198
205
|
}
|
|
199
|
-
console.log(`[ProductsModule] 🌐 获取商品并应用价格: ${cacheKey}`);
|
|
200
206
|
this.logInfo("商品价格缓存未命中,准备获取", { cacheKey });
|
|
201
|
-
const startTime = Date.now();
|
|
202
207
|
const result = await this.prepareProductsWithPrice(schedule_date, extraContext);
|
|
203
|
-
const duration = Date.now() - startTime;
|
|
204
208
|
this.productsPriceCache.set(cacheKey, result);
|
|
205
|
-
console.log(`[ProductsModule] ✅ 商品价格已缓存: ${cacheKey}, 共 ${result.length} 个商品`);
|
|
206
209
|
this.logInfo("商品价格已缓存", {
|
|
207
210
|
cacheKey,
|
|
208
|
-
productCount: result.length
|
|
209
|
-
duration: `${duration}ms`
|
|
211
|
+
productCount: result.length
|
|
210
212
|
});
|
|
211
213
|
this.cleanExpiredPriceCache();
|
|
214
|
+
(0, import_product.perfMark)("getProductsWithPrice(cacheMiss)", performance.now() - t0, {
|
|
215
|
+
cacheKey,
|
|
216
|
+
count: result.length
|
|
217
|
+
});
|
|
212
218
|
return result;
|
|
213
219
|
}
|
|
214
220
|
/**
|
|
@@ -219,27 +225,35 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
219
225
|
* @private
|
|
220
226
|
*/
|
|
221
227
|
async prepareProductsWithPrice(schedule_date, extraContext) {
|
|
228
|
+
const tTotal = performance.now();
|
|
222
229
|
this.logInfo("prepareProductsWithPrice 开始处理", { schedule_date });
|
|
223
230
|
try {
|
|
224
|
-
const allProducts =
|
|
231
|
+
const allProducts = this.getProductsRef();
|
|
225
232
|
this.logInfo("获取到商品列表", { productCount: allProducts.length });
|
|
226
|
-
|
|
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();
|
|
227
240
|
const priceData = await this.loadProductsPrice({
|
|
228
|
-
ids
|
|
241
|
+
ids,
|
|
229
242
|
schedule_date
|
|
230
243
|
});
|
|
231
|
-
|
|
244
|
+
(0, import_product.perfMark)("prepareProducts.loadPrice", performance.now() - tPrice, { count: ids.length });
|
|
232
245
|
this.logInfo("获取商品报价单价格成功", { priceDataCount: (priceData == null ? void 0 : priceData.length) ?? 0 });
|
|
233
246
|
const context = {
|
|
234
247
|
schedule_date,
|
|
235
248
|
priceData,
|
|
236
249
|
...extraContext
|
|
237
|
-
// 合并 Server 层传入的额外数据(如 scheduleList)
|
|
238
250
|
};
|
|
239
|
-
|
|
240
|
-
this.logInfo("开始通过格式化器流程处理商品", { formatterCount: this.formatters.length });
|
|
251
|
+
const tFormat = performance.now();
|
|
241
252
|
const processedProducts = await this.applyFormatters(allProducts, context);
|
|
242
|
-
|
|
253
|
+
(0, import_product.perfMark)("prepareProducts.applyFormatters", performance.now() - tFormat, {
|
|
254
|
+
count: allProducts.length,
|
|
255
|
+
formatterCount: this.formatters.length
|
|
256
|
+
});
|
|
243
257
|
this.logInfo("prepareProductsWithPrice 处理完成", {
|
|
244
258
|
originalProductCount: allProducts.length,
|
|
245
259
|
processedProductCount: processedProducts.length,
|
|
@@ -249,6 +263,10 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
249
263
|
import_types.ProductsHooks.onProductsPriceApplied,
|
|
250
264
|
processedProducts
|
|
251
265
|
);
|
|
266
|
+
(0, import_product.perfMark)("prepareProductsWithPrice", performance.now() - tTotal, {
|
|
267
|
+
productCount: allProducts.length,
|
|
268
|
+
formatterCount: this.formatters.length
|
|
269
|
+
});
|
|
252
270
|
return processedProducts;
|
|
253
271
|
} catch (err) {
|
|
254
272
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
@@ -281,8 +299,13 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
281
299
|
for (let i = 0; i < this.formatters.length; i++) {
|
|
282
300
|
const formatter = this.formatters[i];
|
|
283
301
|
try {
|
|
284
|
-
|
|
302
|
+
const tF = performance.now();
|
|
285
303
|
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
|
+
});
|
|
286
309
|
} catch (error) {
|
|
287
310
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
288
311
|
console.error(`[ProductsModule] ❌ 格式化器 ${i + 1} 执行失败:`, error);
|
|
@@ -293,7 +316,6 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
293
316
|
});
|
|
294
317
|
}
|
|
295
318
|
}
|
|
296
|
-
console.log(`[ProductsModule] ✅ 所有格式化器已应用,共 ${this.formatters.length} 个`);
|
|
297
319
|
this.logInfo("所有格式化器已应用", {
|
|
298
320
|
formatterCount: this.formatters.length,
|
|
299
321
|
resultProductCount: result.length
|
|
@@ -387,10 +409,45 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
387
409
|
console.log("[ProductsModule] 🗑️ 商品价格缓存已清空");
|
|
388
410
|
}
|
|
389
411
|
/**
|
|
390
|
-
*
|
|
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)
|
|
391
447
|
* @param params 查询参数
|
|
448
|
+
* @returns 商品列表
|
|
392
449
|
*/
|
|
393
|
-
async
|
|
450
|
+
async fetchProductsByHttp(params) {
|
|
394
451
|
var _a, _b;
|
|
395
452
|
const {
|
|
396
453
|
category_ids = [],
|
|
@@ -399,7 +456,7 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
399
456
|
customer_id,
|
|
400
457
|
cacheId
|
|
401
458
|
} = params || {};
|
|
402
|
-
this.logInfo("
|
|
459
|
+
this.logInfo("fetchProductsByHttp: 开始请求", {
|
|
403
460
|
categoryIdsCount: category_ids.length,
|
|
404
461
|
productIdsCount: product_ids.length,
|
|
405
462
|
collectionCount: Array.isArray(collection) ? collection.length : collection ? 1 : 0,
|
|
@@ -420,16 +477,12 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
420
477
|
"event_item"
|
|
421
478
|
],
|
|
422
479
|
force_ignore_cache_flag: 1,
|
|
423
|
-
// 获取所有详细信息
|
|
424
480
|
with: [
|
|
425
481
|
"category",
|
|
426
482
|
"collection",
|
|
427
483
|
"resourceRelation",
|
|
428
|
-
// 套餐
|
|
429
484
|
"bundleGroup.bundleItem",
|
|
430
|
-
// 单规格
|
|
431
485
|
"optionGroup.optionItem",
|
|
432
|
-
// 组合规格
|
|
433
486
|
"variantGroup.variantItem"
|
|
434
487
|
],
|
|
435
488
|
open_deposit: 1,
|
|
@@ -449,21 +502,16 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
449
502
|
);
|
|
450
503
|
const productList = ((_b = productsData == null ? void 0 : productsData.data) == null ? void 0 : _b.list) || [];
|
|
451
504
|
const duration = Date.now() - startTime;
|
|
452
|
-
this.logInfo("
|
|
505
|
+
this.logInfo("fetchProductsByHttp: 请求成功", {
|
|
453
506
|
productCount: productList.length,
|
|
454
507
|
duration: `${duration}ms`
|
|
455
508
|
});
|
|
456
|
-
|
|
457
|
-
await this.core.effects.emit(
|
|
458
|
-
import_types.ProductsHooks.onProductsLoaded,
|
|
459
|
-
productsData.data.list
|
|
460
|
-
);
|
|
461
|
-
return productsData.data.list;
|
|
509
|
+
return productList;
|
|
462
510
|
} catch (error) {
|
|
463
511
|
const duration = Date.now() - startTime;
|
|
464
512
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
465
|
-
console.error("[Products]
|
|
466
|
-
this.logError("
|
|
513
|
+
console.error("[Products] fetchProductsByHttp 失败:", error);
|
|
514
|
+
this.logError("fetchProductsByHttp: 请求失败", {
|
|
467
515
|
duration: `${duration}ms`,
|
|
468
516
|
error: errorMessage
|
|
469
517
|
});
|
|
@@ -471,10 +519,33 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
471
519
|
}
|
|
472
520
|
}
|
|
473
521
|
/**
|
|
474
|
-
*
|
|
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
|
+
* 获取商品列表(深拷贝,供外部安全使用)
|
|
475
536
|
*/
|
|
476
537
|
async getProducts() {
|
|
477
|
-
|
|
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;
|
|
478
549
|
}
|
|
479
550
|
/**
|
|
480
551
|
* 根据ID获取单个商品(从内存缓存)
|
|
@@ -482,7 +553,79 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
482
553
|
*/
|
|
483
554
|
async getProductById(id) {
|
|
484
555
|
const product = this.store.map.get(id);
|
|
485
|
-
return product ? (
|
|
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
|
+
}
|
|
486
629
|
}
|
|
487
630
|
/**
|
|
488
631
|
* 清空缓存
|
|
@@ -518,7 +661,9 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
518
661
|
return [];
|
|
519
662
|
}
|
|
520
663
|
try {
|
|
664
|
+
const t0 = performance.now();
|
|
521
665
|
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 });
|
|
522
667
|
this.logInfo("从 IndexDB 加载商品数据", {
|
|
523
668
|
productCount: (products == null ? void 0 : products.length) ?? 0
|
|
524
669
|
});
|
|
@@ -541,11 +686,10 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
541
686
|
}
|
|
542
687
|
this.logInfo("开始保存商品数据到 IndexDB", { productCount: products.length });
|
|
543
688
|
try {
|
|
689
|
+
const t0 = performance.now();
|
|
544
690
|
await this.dbManager.clear(INDEXDB_STORE_NAME);
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
);
|
|
548
|
-
await Promise.all(savePromises);
|
|
691
|
+
await this.dbManager.bulkAdd(INDEXDB_STORE_NAME, products);
|
|
692
|
+
(0, import_product.perfMark)("saveProductsToIndexDB", performance.now() - t0, { count: products.length });
|
|
549
693
|
console.log(
|
|
550
694
|
`[Products] 已将 ${products.length} 个商品平铺保存到 IndexDB`
|
|
551
695
|
);
|
|
@@ -565,11 +709,12 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
565
709
|
* @private
|
|
566
710
|
*/
|
|
567
711
|
syncProductsMap() {
|
|
712
|
+
const t0 = performance.now();
|
|
568
713
|
this.store.map.clear();
|
|
569
714
|
for (const product of this.store.list) {
|
|
570
715
|
this.store.map.set(product.id, product);
|
|
571
716
|
}
|
|
572
|
-
|
|
717
|
+
(0, import_product.perfMark)("syncProductsMap", performance.now() - t0, { count: this.store.map.size });
|
|
573
718
|
}
|
|
574
719
|
/**
|
|
575
720
|
* 预加载模块数据(统一接口)
|
|
@@ -577,22 +722,29 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
577
722
|
*/
|
|
578
723
|
async preload() {
|
|
579
724
|
console.log("[Products] 开始预加载数据...");
|
|
580
|
-
const
|
|
725
|
+
const tTotal = performance.now();
|
|
581
726
|
this.logInfo("开始预加载数据");
|
|
582
727
|
try {
|
|
728
|
+
const tIndexDB = performance.now();
|
|
583
729
|
const cachedData = await this.loadProductsFromIndexDB();
|
|
730
|
+
(0, import_product.perfMark)("preload.loadFromIndexDB", performance.now() - tIndexDB, { count: (cachedData == null ? void 0 : cachedData.length) ?? 0 });
|
|
584
731
|
if (cachedData && cachedData.length > 0) {
|
|
585
732
|
console.log(`[Products] 从 IndexDB 加载了 ${cachedData.length} 个商品`);
|
|
586
|
-
this.store.list =
|
|
733
|
+
this.store.list = cachedData;
|
|
734
|
+
const tSync = performance.now();
|
|
587
735
|
this.syncProductsMap();
|
|
736
|
+
(0, import_product.perfMark)("preload.syncProductsMap", performance.now() - tSync, { count: cachedData.length });
|
|
588
737
|
this.core.effects.emit(
|
|
589
738
|
import_types.ProductsHooks.onProductsChanged,
|
|
590
739
|
this.store.list
|
|
591
740
|
);
|
|
592
|
-
|
|
741
|
+
(0, import_product.perfMark)("preload(IndexDB)", performance.now() - tTotal, {
|
|
742
|
+
count: cachedData.length,
|
|
743
|
+
source: "IndexDB"
|
|
744
|
+
});
|
|
593
745
|
this.logInfo("预加载完成(从 IndexDB)", {
|
|
594
746
|
productCount: cachedData.length,
|
|
595
|
-
duration: `${
|
|
747
|
+
duration: `${Math.round(performance.now() - tTotal)}ms`,
|
|
596
748
|
source: "IndexDB"
|
|
597
749
|
});
|
|
598
750
|
return;
|
|
@@ -604,25 +756,417 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
604
756
|
console.warn("[Products] 从 IndexDB 加载数据失败:", error);
|
|
605
757
|
this.logWarning("从 IndexDB 加载数据失败,准备从服务器加载", { error: errorMessage });
|
|
606
758
|
}
|
|
759
|
+
const tServer = performance.now();
|
|
607
760
|
const products = await this.loadProductsByServer();
|
|
761
|
+
(0, import_product.perfMark)("preload.loadFromServer", performance.now() - tServer, { count: (products == null ? void 0 : products.length) ?? 0 });
|
|
608
762
|
if (products && products.length > 0) {
|
|
609
|
-
|
|
610
|
-
|
|
763
|
+
this.store.list = products;
|
|
764
|
+
const tSync = performance.now();
|
|
611
765
|
this.syncProductsMap();
|
|
766
|
+
(0, import_product.perfMark)("preload.syncProductsMap", performance.now() - tSync, { count: products.length });
|
|
612
767
|
this.core.effects.emit(import_types.ProductsHooks.onProductsChanged, this.store.list);
|
|
613
|
-
|
|
768
|
+
(0, import_product.perfMark)("preload(Server)", performance.now() - tTotal, {
|
|
769
|
+
count: products.length,
|
|
770
|
+
source: "Server"
|
|
771
|
+
});
|
|
614
772
|
this.logInfo("预加载完成(从服务器)", {
|
|
615
773
|
productCount: products.length,
|
|
616
|
-
duration: `${
|
|
774
|
+
duration: `${Math.round(performance.now() - tTotal)}ms`,
|
|
617
775
|
source: "Server"
|
|
618
776
|
});
|
|
619
777
|
} else {
|
|
620
|
-
|
|
778
|
+
(0, import_product.perfMark)("preload(empty)", performance.now() - tTotal, { source: "empty" });
|
|
621
779
|
this.logWarning("预加载完成但未获取到数据", {
|
|
622
|
-
duration: `${
|
|
780
|
+
duration: `${Math.round(performance.now() - tTotal)}ms`
|
|
623
781
|
});
|
|
624
782
|
}
|
|
625
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
|
|
1151
|
+
});
|
|
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
|
+
}
|
|
1167
|
+
this.pendingSyncMessages = [];
|
|
1168
|
+
this.logInfo("destroyProductSync: 同步资源已销毁");
|
|
1169
|
+
}
|
|
626
1170
|
/**
|
|
627
1171
|
* 获取模块的路由定义
|
|
628
1172
|
* Products 模块暂不提供路由,由 Server 层统一处理
|