@pisell/pisellos 2.2.94 → 2.2.95

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 (39) hide show
  1. package/dist/core/index.d.ts +1 -0
  2. package/dist/core/index.js +7 -0
  3. package/dist/modules/Customer/index.d.ts +1 -0
  4. package/dist/modules/Customer/index.js +28 -12
  5. package/dist/modules/Order/index.d.ts +1 -1
  6. package/dist/server/index.d.ts +6 -1
  7. package/dist/server/index.js +40 -17
  8. package/dist/server/modules/order/types.d.ts +1 -1
  9. package/dist/server/modules/order/utils/filterBookings.js +1 -1
  10. package/dist/server/modules/products/index.d.ts +19 -24
  11. package/dist/server/modules/products/index.js +429 -600
  12. package/dist/server/modules/products/types.d.ts +1 -0
  13. package/dist/server/utils/product.d.ts +4 -0
  14. package/dist/server/utils/product.js +34 -0
  15. package/dist/solution/BookingByStep/index.d.ts +1 -1
  16. package/dist/solution/Sales/index.d.ts +1 -1
  17. package/dist/solution/Sales/index.js +72 -20
  18. package/dist/solution/Sales/types.d.ts +5 -4
  19. package/dist/types/index.d.ts +2 -0
  20. package/lib/core/index.d.ts +1 -0
  21. package/lib/core/index.js +4 -0
  22. package/lib/modules/Customer/index.d.ts +1 -0
  23. package/lib/modules/Customer/index.js +21 -6
  24. package/lib/modules/Order/index.d.ts +1 -1
  25. package/lib/server/index.d.ts +6 -1
  26. package/lib/server/index.js +33 -13
  27. package/lib/server/modules/order/types.d.ts +1 -1
  28. package/lib/server/modules/order/utils/filterBookings.js +1 -1
  29. package/lib/server/modules/products/index.d.ts +19 -24
  30. package/lib/server/modules/products/index.js +151 -150
  31. package/lib/server/modules/products/types.d.ts +1 -0
  32. package/lib/server/utils/product.d.ts +4 -0
  33. package/lib/server/utils/product.js +27 -0
  34. package/lib/solution/BookingByStep/index.d.ts +1 -1
  35. package/lib/solution/Sales/index.d.ts +1 -1
  36. package/lib/solution/Sales/index.js +67 -12
  37. package/lib/solution/Sales/types.d.ts +5 -4
  38. package/lib/types/index.d.ts +2 -0
  39. package/package.json +1 -1
@@ -53,14 +53,20 @@ export declare class ProductsModule extends BaseModule implements Module {
53
53
  * 缓存的是已经应用了价格的完整商品列表,避免重复转换
54
54
  * @param schedule_date 日期
55
55
  * @param extraContext 额外的上下文数据(可选,由 Server 层传入)
56
+ * @param options 可选参数
57
+ * @param options.changedIds 变更的商品 IDs,非空时仅对这些商品增量执行 prepare 并更新缓存
56
58
  * @returns 应用了价格的商品列表
57
59
  */
58
- getProductsWithPrice(schedule_date: string, extraContext?: Partial<ProductFormatterContext>): Promise<ProductData[]>;
60
+ getProductsWithPrice(schedule_date: string, extraContext?: Partial<ProductFormatterContext>, options?: {
61
+ changedIds?: number[];
62
+ }): Promise<ProductData[]>;
59
63
  /**
60
64
  * 准备带价格的商品数据(通过格式化器流程处理)
61
65
  * @param schedule_date 日期
62
66
  * @param extraContext 额外的上下文数据(可选)
63
- * @returns 完整处理后的商品列表
67
+ * @param options 可选参数
68
+ * @param options.productIds 指定商品 IDs,仅处理这些商品;不传则处理全量
69
+ * @returns 处理后的商品列表
64
70
  * @private
65
71
  */
66
72
  private prepareProductsWithPrice;
@@ -156,11 +162,6 @@ export declare class ProductsModule extends BaseModule implements Module {
156
162
  * 用于 pubsub 同步 create / update / batch_update 场景
157
163
  */
158
164
  refreshProducts(): Promise<ProductData[]>;
159
- /**
160
- * 局部更新指定商品的报价单价格
161
- * 遍历所有已缓存的日期,为目标商品重新获取价格并覆盖到缓存中
162
- */
163
- updateProductPriceByIds(ids: number[]): Promise<void>;
164
165
  /**
165
166
  * 清空缓存
166
167
  */
@@ -202,15 +203,18 @@ export declare class ProductsModule extends BaseModule implements Module {
202
203
  *
203
204
  * product 模块:
204
205
  * - operation === 'delete' → 本地删除
205
- * - body(无 price change_types) body 完整数据直接覆盖本地
206
- * - change_types 包含 price SSE 增量拉取 + 刷新报价单价格缓存
207
- * - change_types stock → 跳过(暂不响应)
206
+ * - change_types 包含 price → 仅收集变更 IDs(不拉商品数据)
207
+ * - bodybody 完整数据直接覆盖本地
208
+ * - 其他 SSE 增量拉取
208
209
  *
209
- * product_collection / product_category / product_quotation
210
+ * product_collection / product_category:
210
211
  * - 按 relation_product_ids SSE 拉取受影响商品
211
- * - product_quotation 额外刷新报价单价格缓存
212
212
  *
213
- * 处理完成后 emit onProductsSyncCompleted 通知 Server 层
213
+ * product_quotation:
214
+ * - 报价单变更影响范围大,直接清除价格缓存走全量重建
215
+ *
216
+ * 处理完成后 emit onProductsSyncCompleted(携带 changedIds),
217
+ * Server 层监听该事件后对变更商品增量执行 prepareProductsWithPrice 并更新缓存
214
218
  */
215
219
  private processProductSyncMessages;
216
220
  /**
@@ -221,7 +225,8 @@ export declare class ProductsModule extends BaseModule implements Module {
221
225
  /**
222
226
  * 将 body 完整数据直接覆盖到本地 store(不调用报价单接口)
223
227
  * 已存在的 → 直接替换;不存在的 → 追加
224
- * 同时更新 Map 缓存、IndexDB,清空价格缓存,触发 onProductsChanged
228
+ * 同时更新 Map 缓存、IndexDB,触发 onProductsChanged
229
+ * 价格缓存由 processProductSyncMessages 末尾统一清除
225
230
  */
226
231
  private applyBodyUpdatesToStore;
227
232
  /**
@@ -230,16 +235,6 @@ export declare class ProductsModule extends BaseModule implements Module {
230
235
  * 同时更新 store.map、IndexDB,触发 onProductsChanged
231
236
  */
232
237
  private mergeProductsToStore;
233
- /**
234
- * 增量更新价格缓存中变更的商品
235
- * 对每个已缓存的日期 key:替换/追加最新商品数据,重新拉取这些 ID 的价格并应用
236
- */
237
- private updatePriceCacheForProducts;
238
- /**
239
- * 全量重新拉取报价单价格并重建价格缓存
240
- * 遍历当前已缓存的所有日期 key,对每个日期重新调用 loadProductsPrice
241
- */
242
- private refreshAllPriceCache;
243
238
  /**
244
239
  * 静默全量刷新:后台重新拉取全量 SSE 数据并更新本地
245
240
  * 拿到完整数据后一次性替换 store,清除价格缓存,触发 onProductsSyncCompleted
@@ -177,7 +177,7 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
177
177
  duration: `${duration}ms`,
178
178
  error: errorMessage
179
179
  });
180
- throw error;
180
+ return [];
181
181
  }
182
182
  }
183
183
  /**
@@ -186,13 +186,70 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
186
186
  * 缓存的是已经应用了价格的完整商品列表,避免重复转换
187
187
  * @param schedule_date 日期
188
188
  * @param extraContext 额外的上下文数据(可选,由 Server 层传入)
189
+ * @param options 可选参数
190
+ * @param options.changedIds 变更的商品 IDs,非空时仅对这些商品增量执行 prepare 并更新缓存
189
191
  * @returns 应用了价格的商品列表
190
192
  */
191
- async getProductsWithPrice(schedule_date, extraContext) {
193
+ async getProductsWithPrice(schedule_date, extraContext, options) {
192
194
  const t0 = performance.now();
193
195
  const cacheKey = schedule_date;
196
+ const changedIds = options == null ? void 0 : options.changedIds;
194
197
  if (this.productsPriceCache.has(cacheKey)) {
195
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
+ }
196
253
  (0, import_product.perfMark)("getProductsWithPrice(cacheHit)", performance.now() - t0, {
197
254
  cacheKey,
198
255
  count: cachedProducts.length
@@ -221,21 +278,38 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
221
278
  * 准备带价格的商品数据(通过格式化器流程处理)
222
279
  * @param schedule_date 日期
223
280
  * @param extraContext 额外的上下文数据(可选)
224
- * @returns 完整处理后的商品列表
281
+ * @param options 可选参数
282
+ * @param options.productIds 指定商品 IDs,仅处理这些商品;不传则处理全量
283
+ * @returns 处理后的商品列表
225
284
  * @private
226
285
  */
227
- async prepareProductsWithPrice(schedule_date, extraContext) {
286
+ async prepareProductsWithPrice(schedule_date, extraContext, options) {
287
+ var _a, _b;
228
288
  const tTotal = performance.now();
229
- this.logInfo("prepareProductsWithPrice 开始处理", { schedule_date });
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
+ });
230
296
  try {
231
- const allProducts = this.getProductsRef();
232
- 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;
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 });
237
311
  }
238
- (0, import_product.perfMark)("prepareProducts.extractIds", performance.now() - tIds, { count: ids.length });
312
+ this.logInfo("获取到商品列表", { productCount: products.length });
239
313
  const tPrice = performance.now();
240
314
  const priceData = await this.loadProductsPrice({
241
315
  ids,
@@ -246,26 +320,31 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
246
320
  const context = {
247
321
  schedule_date,
248
322
  priceData,
323
+ locale: (_b = (_a = this.core) == null ? void 0 : _a.context) == null ? void 0 : _b.locale,
249
324
  ...extraContext
250
325
  };
251
326
  const tFormat = performance.now();
252
- const processedProducts = await this.applyFormatters(allProducts, context);
327
+ const processedProducts = await this.applyFormatters(products, context);
253
328
  (0, import_product.perfMark)("prepareProducts.applyFormatters", performance.now() - tFormat, {
254
- count: allProducts.length,
329
+ count: products.length,
255
330
  formatterCount: this.formatters.length
256
331
  });
257
332
  this.logInfo("prepareProductsWithPrice 处理完成", {
258
- originalProductCount: allProducts.length,
333
+ mode: isIncremental ? "incremental" : "full",
334
+ originalProductCount: products.length,
259
335
  processedProductCount: processedProducts.length,
260
336
  formatterCount: this.formatters.length
261
337
  });
262
- await this.core.effects.emit(
263
- import_types.ProductsHooks.onProductsPriceApplied,
264
- processedProducts
265
- );
338
+ if (!isIncremental) {
339
+ await this.core.effects.emit(
340
+ import_types.ProductsHooks.onProductsPriceApplied,
341
+ processedProducts
342
+ );
343
+ }
266
344
  (0, import_product.perfMark)("prepareProductsWithPrice", performance.now() - tTotal, {
267
- productCount: allProducts.length,
268
- formatterCount: this.formatters.length
345
+ productCount: products.length,
346
+ formatterCount: this.formatters.length,
347
+ mode: isIncremental ? "incremental" : "full"
269
348
  });
270
349
  return processedProducts;
271
350
  } catch (err) {
@@ -340,10 +419,14 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
340
419
  console.log(`[ProductsModule] 💰 应用价格数据到 ${products.length} 个商品`);
341
420
  return (0, import_product.applyPriceDataToProducts)(products, context.priceData);
342
421
  };
422
+ const i18nFormatter = (products, context) => {
423
+ return (0, import_product.applyI18nToProducts)(products, context.locale);
424
+ };
343
425
  const detailValueFormatter = (products, context) => {
344
426
  return (0, import_product.applyDetailValueToProducts)(products, context);
345
427
  };
346
428
  this.formatters.unshift(priceFormatter);
429
+ this.formatters.push(i18nFormatter);
347
430
  this.formatters.push(detailValueFormatter);
348
431
  this.isPriceFormatterRegistered = true;
349
432
  console.log("[ProductsModule] ✅ 内置价格格式化器已注册(第 1 个)");
@@ -604,29 +687,6 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
604
687
  (0, import_product.perfMark)("refreshProducts", performance.now() - tTotal, { count: this.store.list.length });
605
688
  return this.store.list;
606
689
  }
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
- }
629
- }
630
690
  /**
631
691
  * 清空缓存
632
692
  */
@@ -688,7 +748,7 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
688
748
  try {
689
749
  const t0 = performance.now();
690
750
  await this.dbManager.clear(INDEXDB_STORE_NAME);
691
- await this.dbManager.bulkAdd(INDEXDB_STORE_NAME, products);
751
+ await this.dbManager.bulkUpdate(INDEXDB_STORE_NAME, products);
692
752
  (0, import_product.perfMark)("saveProductsToIndexDB", performance.now() - t0, { count: products.length });
693
753
  console.log(
694
754
  `[Products] 已将 ${products.length} 个商品平铺保存到 IndexDB`
@@ -853,18 +913,21 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
853
913
  *
854
914
  * product 模块:
855
915
  * - operation === 'delete' → 本地删除
856
- * - body(无 price change_types) body 完整数据直接覆盖本地
857
- * - change_types 包含 price SSE 增量拉取 + 刷新报价单价格缓存
858
- * - change_types stock → 跳过(暂不响应)
916
+ * - change_types 包含 price → 仅收集变更 IDs(不拉商品数据)
917
+ * - bodybody 完整数据直接覆盖本地
918
+ * - 其他 SSE 增量拉取
859
919
  *
860
- * product_collection / product_category / product_quotation
920
+ * product_collection / product_category:
861
921
  * - 按 relation_product_ids SSE 拉取受影响商品
862
- * - product_quotation 额外刷新报价单价格缓存
863
922
  *
864
- * 处理完成后 emit onProductsSyncCompleted 通知 Server 层
923
+ * product_quotation:
924
+ * - 报价单变更影响范围大,直接清除价格缓存走全量重建
925
+ *
926
+ * 处理完成后 emit onProductsSyncCompleted(携带 changedIds),
927
+ * Server 层监听该事件后对变更商品增量执行 prepareProductsWithPrice 并更新缓存
865
928
  */
866
929
  async processProductSyncMessages() {
867
- var _a, _b, _c, _d, _e;
930
+ var _a, _b, _c, _d, _e, _f;
868
931
  const messages = [...this.pendingSyncMessages];
869
932
  this.pendingSyncMessages = [];
870
933
  if (messages.length === 0)
@@ -874,24 +937,28 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
874
937
  const bodyUpdates = /* @__PURE__ */ new Map();
875
938
  const sseRefreshIds = [];
876
939
  const priceRefreshIds = [];
940
+ let shouldClearPriceCache = false;
877
941
  for (const msg of messages) {
878
942
  const channelKey = msg._channelKey || msg.module || "product";
879
943
  if (channelKey === "product") {
944
+ if ((_a = msg.relation_product_ids) == null ? void 0 : _a.length) {
945
+ sseRefreshIds.push(...msg.relation_product_ids);
946
+ }
880
947
  if (msg.operation === "delete" || msg.action === "delete") {
881
- if ((_a = msg.ids) == null ? void 0 : _a.length)
948
+ if ((_b = msg.ids) == null ? void 0 : _b.length)
882
949
  deleteIds.push(...msg.ids);
883
950
  else if (msg.id)
884
951
  deleteIds.push(msg.id);
885
952
  continue;
886
953
  }
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
954
  if ((_c = msg.change_types) == null ? void 0 : _c.includes("price")) {
892
955
  const ids = msg.ids || (msg.id ? [msg.id] : []);
893
- sseRefreshIds.push(...ids);
894
956
  priceRefreshIds.push(...ids);
957
+ if (msg.body) {
958
+ const bodyId = msg.body.id;
959
+ if (bodyId)
960
+ bodyUpdates.set(bodyId, msg.body);
961
+ }
895
962
  continue;
896
963
  }
897
964
  if (msg.body) {
@@ -905,13 +972,14 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
905
972
  } else if (msg.id) {
906
973
  sseRefreshIds.push(msg.id);
907
974
  }
908
- } else if (["product_collection", "product_category", "product_quotation"].includes(channelKey)) {
975
+ } else if (channelKey === "product_quotation") {
976
+ shouldClearPriceCache = true;
909
977
  if ((_e = msg.relation_product_ids) == null ? void 0 : _e.length) {
910
978
  sseRefreshIds.push(...msg.relation_product_ids);
911
- if (channelKey === "product_quotation") {
912
- this.clearPriceCache();
913
- priceRefreshIds.push(...msg.relation_product_ids);
914
- }
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);
915
983
  }
916
984
  }
917
985
  }
@@ -928,29 +996,36 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
928
996
  const freshProducts = await this.fetchProductsBySSE(uniqueSSEIds);
929
997
  if (freshProducts.length > 0) {
930
998
  await this.mergeProductsToStore(freshProducts);
931
- await this.updatePriceCacheForProducts(freshProducts);
932
999
  }
933
- this.logInfo("processProductSyncMessages: SSE 增量更新完成", {
1000
+ this.logInfo("processProductSyncMessages: SSE 增量更新完成123", {
934
1001
  requestedCount: uniqueSSEIds.length,
935
1002
  receivedCount: freshProducts.length
936
1003
  });
937
1004
  }
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: 处理完成", {
1005
+ const allChangedIds = [.../* @__PURE__ */ new Set([
1006
+ ...Array.from(bodyUpdates.keys()),
1007
+ ...uniqueSSEIds,
1008
+ ...uniquePriceIds
1009
+ ])];
1010
+ this.logInfo("processProductSyncMessages: 处理完成123", {
944
1011
  deleteCount: uniqueDeleteIds.length,
945
1012
  bodyUpdateCount: bodyUpdates.size,
946
1013
  sseRefreshCount: uniqueSSEIds.length,
947
- priceRefreshCount: uniquePriceIds.length
1014
+ priceRefreshCount: uniquePriceIds.length,
1015
+ allChangedIdsCount: allChangedIds.length,
1016
+ shouldClearPriceCache
948
1017
  });
949
- if (uniqueDeleteIds.length === 0 && bodyUpdates.size === 0 && uniqueSSEIds.length === 0 && uniquePriceIds.length === 0) {
1018
+ const hasChanges = uniqueDeleteIds.length > 0 || allChangedIds.length > 0 || shouldClearPriceCache;
1019
+ if (!hasChanges) {
950
1020
  this.logInfo("processProductSyncMessages: 没有变更,不触发 onProductsSyncCompleted");
951
1021
  return;
952
1022
  }
953
- await this.core.effects.emit(import_types.ProductsHooks.onProductsSyncCompleted, null);
1023
+ if (shouldClearPriceCache) {
1024
+ this.clearPriceCache();
1025
+ }
1026
+ await this.core.effects.emit(import_types.ProductsHooks.onProductsSyncCompleted, {
1027
+ changedIds: allChangedIds
1028
+ });
954
1029
  }
955
1030
  /**
956
1031
  * 通过 SSE 按 ids 增量拉取商品数据
@@ -984,7 +1059,8 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
984
1059
  /**
985
1060
  * 将 body 完整数据直接覆盖到本地 store(不调用报价单接口)
986
1061
  * 已存在的 → 直接替换;不存在的 → 追加
987
- * 同时更新 Map 缓存、IndexDB,清空价格缓存,触发 onProductsChanged
1062
+ * 同时更新 Map 缓存、IndexDB,触发 onProductsChanged
1063
+ * 价格缓存由 processProductSyncMessages 末尾统一清除
988
1064
  */
989
1065
  async applyBodyUpdatesToStore(bodyUpdates) {
990
1066
  this.logInfo("applyBodyUpdatesToStore: 开始", { count: bodyUpdates.size });
@@ -1007,7 +1083,6 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
1007
1083
  }
1008
1084
  this.syncProductsMap();
1009
1085
  await this.saveProductsToIndexDB(this.store.list);
1010
- await this.updatePriceCacheForProducts([...bodyUpdates.values()]);
1011
1086
  this.core.effects.emit(import_types.ProductsHooks.onProductsChanged, this.store.list);
1012
1087
  this.logInfo("applyBodyUpdatesToStore: 完成", {
1013
1088
  updatedCount,
@@ -1048,80 +1123,6 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
1048
1123
  });
1049
1124
  this.core.effects.emit(import_types.ProductsHooks.onProductsChanged, this.store.list);
1050
1125
  }
1051
- /**
1052
- * 增量更新价格缓存中变更的商品
1053
- * 对每个已缓存的日期 key:替换/追加最新商品数据,重新拉取这些 ID 的价格并应用
1054
- */
1055
- async updatePriceCacheForProducts(freshProducts) {
1056
- if (this.productsPriceCache.size === 0)
1057
- return;
1058
- const freshIds = freshProducts.map((p) => p.id);
1059
- const freshMap = /* @__PURE__ */ new Map();
1060
- for (const p of freshProducts) {
1061
- freshMap.set(p.id, p);
1062
- }
1063
- this.logInfo("updatePriceCacheForProducts: 开始", {
1064
- freshIds,
1065
- cachedDateCount: this.productsPriceCache.size
1066
- });
1067
- for (const [dateKey, cachedProducts] of this.productsPriceCache.entries()) {
1068
- try {
1069
- const updatedList = cachedProducts.map((p) => {
1070
- const fresh = freshMap.get(p.id);
1071
- return fresh ? { ...fresh } : p;
1072
- });
1073
- const existingIds = new Set(cachedProducts.map((p) => p.id));
1074
- for (const p of freshProducts) {
1075
- if (!existingIds.has(p.id)) {
1076
- updatedList.push({ ...p });
1077
- }
1078
- }
1079
- const priceData = await this.loadProductsPrice({
1080
- ids: freshIds,
1081
- schedule_date: dateKey
1082
- });
1083
- const result = priceData && priceData.length > 0 ? (0, import_product.applyPriceDataToProducts)(updatedList, priceData) : updatedList;
1084
- this.productsPriceCache.set(dateKey, result);
1085
- this.logInfo("updatePriceCacheForProducts: 日期缓存已更新", {
1086
- dateKey,
1087
- updatedIds: freshIds
1088
- });
1089
- } catch (error) {
1090
- const errorMessage = error instanceof Error ? error.message : String(error);
1091
- this.logError("updatePriceCacheForProducts: 失败", { dateKey, error: errorMessage });
1092
- }
1093
- }
1094
- }
1095
- /**
1096
- * 全量重新拉取报价单价格并重建价格缓存
1097
- * 遍历当前已缓存的所有日期 key,对每个日期重新调用 loadProductsPrice
1098
- */
1099
- async refreshAllPriceCache() {
1100
- const allProducts = this.getProductsRef();
1101
- if (allProducts.length === 0)
1102
- return;
1103
- const ids = allProducts.map((p) => p.id);
1104
- const dateKeys = Array.from(this.productsPriceCache.keys());
1105
- this.clearPriceCache();
1106
- if (dateKeys.length === 0) {
1107
- this.logInfo("refreshAllPriceCache: 无已缓存日期,跳过");
1108
- return;
1109
- }
1110
- this.logInfo("refreshAllPriceCache: 开始重新拉取", { dateKeys, productCount: ids.length });
1111
- for (const dateKey of dateKeys) {
1112
- try {
1113
- const priceData = await this.loadProductsPrice({ ids, schedule_date: dateKey });
1114
- if (priceData && priceData.length > 0) {
1115
- const updatedProducts = (0, import_product.applyPriceDataToProducts)(allProducts, priceData);
1116
- this.productsPriceCache.set(dateKey, updatedProducts);
1117
- }
1118
- this.logInfo("refreshAllPriceCache: 日期缓存已更新", { dateKey });
1119
- } catch (error) {
1120
- const errorMessage = error instanceof Error ? error.message : String(error);
1121
- this.logError("refreshAllPriceCache: 失败", { dateKey, error: errorMessage });
1122
- }
1123
- }
1124
- }
1125
1126
  /**
1126
1127
  * 静默全量刷新:后台重新拉取全量 SSE 数据并更新本地
1127
1128
  * 拿到完整数据后一次性替换 store,清除价格缓存,触发 onProductsSyncCompleted
@@ -109,6 +109,7 @@ export interface ProductFormatterContext {
109
109
  schedule_date: string;
110
110
  priceData?: LoadProductsPriceData[];
111
111
  scheduleModule?: any;
112
+ locale?: string;
112
113
  }
113
114
  /**
114
115
  * 商品格式化器类型
@@ -10,6 +10,10 @@ export declare function perfMark(label: string, durationMs: number, meta?: Recor
10
10
  */
11
11
  export declare function applyPriceDataToProducts(products: ProductData[], priceData: LoadProductsPriceData[]): ProductData[];
12
12
  export declare const getIsSessionProduct: (product: ProductData) => boolean;
13
+ /**
14
+ * 根据 locale 将商品的 i18n 字段覆盖到对应原始字段
15
+ */
16
+ export declare function applyI18nToProducts(products: ProductData[], locale?: string): ProductData[];
13
17
  /**
14
18
  * 将详情值数据应用到商品列表
15
19
  * @param products 商品列表
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  var product_exports = {};
21
21
  __export(product_exports, {
22
22
  applyDetailValueToProducts: () => applyDetailValueToProducts,
23
+ applyI18nToProducts: () => applyI18nToProducts,
23
24
  applyPriceDataToProducts: () => applyPriceDataToProducts,
24
25
  getIsSessionProduct: () => getIsSessionProduct,
25
26
  perfMark: () => perfMark
@@ -155,6 +156,31 @@ var getIsOpenDetailModal = (product, context) => {
155
156
  scheduleTimeSlots
156
157
  };
157
158
  };
159
+ var I18N_FIELD_MAP = {
160
+ title_i18n: "title",
161
+ subtitle_i18n: "subtitle",
162
+ description_i18n: "description",
163
+ cover_i18n: "cover"
164
+ };
165
+ function applyI18nToProducts(products, locale) {
166
+ if (!locale)
167
+ return products;
168
+ return products.map((product) => {
169
+ let patched = null;
170
+ for (const [i18nKey, originalKey] of Object.entries(I18N_FIELD_MAP)) {
171
+ const i18nDict = product[i18nKey];
172
+ if (!i18nDict)
173
+ continue;
174
+ const localizedValue = i18nDict[locale];
175
+ if (localizedValue) {
176
+ if (!patched)
177
+ patched = { ...product };
178
+ patched[originalKey] = localizedValue;
179
+ }
180
+ }
181
+ return patched ?? product;
182
+ });
183
+ }
158
184
  var formatDataKey = (data) => {
159
185
  var _a, _b, _c, _d;
160
186
  const _data = {
@@ -258,6 +284,7 @@ function applyDetailValueToProducts(products, context) {
258
284
  // Annotate the CommonJS export names for ESM import in node:
259
285
  0 && (module.exports = {
260
286
  applyDetailValueToProducts,
287
+ applyI18nToProducts,
261
288
  applyPriceDataToProducts,
262
289
  getIsSessionProduct,
263
290
  perfMark
@@ -344,7 +344,7 @@ export declare class BookingByStepImpl extends BaseModule implements Module {
344
344
  };
345
345
  setOtherData(key: string, value: any): void;
346
346
  getOtherData(key: string): any;
347
- getProductTypeById(id: number): Promise<"normal" | "duration" | "session">;
347
+ getProductTypeById(id: number): Promise<"duration" | "session" | "normal">;
348
348
  /**
349
349
  * 提供给 UI 的方法,减轻 UI 层的计算压力,UI 层只需要传递 cartItemId 和 resourceCode 即返回对应的 renderList
350
350
  *
@@ -58,7 +58,7 @@ export declare class SalesImpl extends BaseModule implements Module, SalesModule
58
58
  * - n = booking 数量
59
59
  * - s = 时间片数量
60
60
  */
61
- getTimelineHighlights(bookingList?: BookingData[]): SalesTimelineHighlights;
61
+ getTimelineHighlights(bookingList?: BookingData[], startDateTime?: string, endDateTime?: string): SalesTimelineHighlights;
62
62
  /** dayjs 未启用插件时,手动封装 >= 判断 */
63
63
  private isSameOrAfter;
64
64
  /** dayjs 未启用插件时,手动封装 <= 判断 */