@pisell/pisellos 2.2.88 → 2.2.90

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.
@@ -154,11 +154,16 @@ declare class Server {
154
154
  /**
155
155
  * 商品查询的核心计算逻辑(编排 Products、Menu、Schedule 模块)
156
156
  * 供 handleProductQuery 首次返回及 pubsub 变更推送复用
157
+ * @param context 查询上下文
158
+ * @param options 可选参数
159
+ * @param options.changedIds 变更的商品 IDs,用于增量更新价格缓存
157
160
  */
158
161
  private computeProductQueryResult;
159
162
  /**
160
163
  * 数据变更后,遍历所有订阅者重新计算查询结果并通过 callback 推送
161
164
  * 由 ProductsModule 的 onProductsSyncCompleted 事件触发
165
+ * @param options 可选参数
166
+ * @param options.changedIds 变更的商品 IDs,用于增量更新价格缓存
162
167
  */
163
168
  private recomputeAndNotifyProductQuery;
164
169
  /**
@@ -415,8 +415,10 @@ var Server = class {
415
415
  } else {
416
416
  this.logInfo("跳过自动预加载", { autoPreload });
417
417
  }
418
- this.core.effects.on(import_types.ProductsHooks.onProductsSyncCompleted, () => {
419
- this.recomputeAndNotifyProductQuery();
418
+ this.core.effects.on(import_types.ProductsHooks.onProductsSyncCompleted, (payload) => {
419
+ this.recomputeAndNotifyProductQuery({
420
+ changedIds: payload == null ? void 0 : payload.changedIds
421
+ });
420
422
  });
421
423
  const duration = Date.now() - startTime;
422
424
  this.logInfo("Server 初始化完成", {
@@ -654,14 +656,18 @@ var Server = class {
654
656
  /**
655
657
  * 商品查询的核心计算逻辑(编排 Products、Menu、Schedule 模块)
656
658
  * 供 handleProductQuery 首次返回及 pubsub 变更推送复用
659
+ * @param context 查询上下文
660
+ * @param options 可选参数
661
+ * @param options.changedIds 变更的商品 IDs,用于增量更新价格缓存
657
662
  */
658
- async computeProductQueryResult(context) {
663
+ async computeProductQueryResult(context, options) {
659
664
  const tTotal = performance.now();
660
665
  const { menu_list_ids, schedule_date, schedule_datetime } = context;
661
666
  this.logInfo("computeProductQueryResult 开始", {
662
667
  menuListIdsCount: (menu_list_ids == null ? void 0 : menu_list_ids.length) ?? 0,
663
668
  schedule_datetime,
664
- schedule_date
669
+ schedule_date,
670
+ changedIds: options == null ? void 0 : options.changedIds
665
671
  });
666
672
  if (!this.products) {
667
673
  this.logError("computeProductQueryResult: Products 模块未注册");
@@ -691,6 +697,8 @@ var Server = class {
691
697
  const tPrice = performance.now();
692
698
  const allProductsWithPrice = await this.products.getProductsWithPrice(schedule_date, {
693
699
  scheduleModule: this.getSchedule()
700
+ }, {
701
+ changedIds: options == null ? void 0 : options.changedIds
694
702
  });
695
703
  (0, import_product.perfMark)("computeQuery.getProductsWithPrice", performance.now() - tPrice, {
696
704
  count: allProductsWithPrice.length
@@ -736,16 +744,21 @@ var Server = class {
736
744
  /**
737
745
  * 数据变更后,遍历所有订阅者重新计算查询结果并通过 callback 推送
738
746
  * 由 ProductsModule 的 onProductsSyncCompleted 事件触发
747
+ * @param options 可选参数
748
+ * @param options.changedIds 变更的商品 IDs,用于增量更新价格缓存
739
749
  */
740
- async recomputeAndNotifyProductQuery() {
750
+ async recomputeAndNotifyProductQuery(options) {
741
751
  if (this.productQuerySubscribers.size === 0)
742
752
  return;
743
753
  this.logInfo("recomputeAndNotifyProductQuery: 开始推送", {
744
- subscriberCount: this.productQuerySubscribers.size
754
+ subscriberCount: this.productQuerySubscribers.size,
755
+ changedIds: options == null ? void 0 : options.changedIds
745
756
  });
746
757
  for (const [subscriberId, subscriber] of this.productQuerySubscribers.entries()) {
747
758
  try {
748
- const result = await this.computeProductQueryResult(subscriber.context);
759
+ const result = await this.computeProductQueryResult(subscriber.context, {
760
+ changedIds: options == null ? void 0 : options.changedIds
761
+ });
749
762
  subscriber.callback(result);
750
763
  this.logInfo("recomputeAndNotifyProductQuery: 已推送", { subscriberId });
751
764
  } catch (error) {
@@ -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
- * 后续查询会走完整的 formatter 管道重建缓存
162
- */
163
- updateProductPriceByIds(ids: number[]): Promise<void>;
164
165
  /**
165
166
  * 清空缓存
166
167
  */
@@ -202,14 +203,18 @@ export declare class ProductsModule extends BaseModule implements Module {
202
203
  *
203
204
  * product 模块:
204
205
  * - operation === 'delete' → 本地删除
205
- * - change_types 包含 price → SSE 增量拉取
206
+ * - change_types 包含 price → 仅收集变更 IDs(不拉商品数据)
206
207
  * - 有 body → body 完整数据直接覆盖本地
208
+ * - 其他 → SSE 增量拉取
207
209
  *
208
- * product_collection / product_category / product_quotation
210
+ * product_collection / product_category:
209
211
  * - 按 relation_product_ids SSE 拉取受影响商品
210
212
  *
211
- * 处理完成后清除价格缓存并 emit onProductsSyncCompleted,
212
- * Server 层监听该事件后重新查询,走完整的 formatter 管道重建缓存
213
+ * product_quotation:
214
+ * - 报价单变更影响范围大,直接清除价格缓存走全量重建
215
+ *
216
+ * 处理完成后 emit onProductsSyncCompleted(携带 changedIds),
217
+ * Server 层监听该事件后对变更商品增量执行 prepareProductsWithPrice 并更新缓存
213
218
  */
214
219
  private processProductSyncMessages;
215
220
  /**
@@ -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,37 @@ 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) {
228
287
  const tTotal = performance.now();
229
- this.logInfo("prepareProductsWithPrice 开始处理", { schedule_date });
288
+ const targetIds = options == null ? void 0 : options.productIds;
289
+ const isIncremental = targetIds && targetIds.length > 0;
290
+ this.logInfo("prepareProductsWithPrice 开始处理", {
291
+ schedule_date,
292
+ mode: isIncremental ? "incremental" : "full",
293
+ targetIdsCount: targetIds == null ? void 0 : targetIds.length
294
+ });
230
295
  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;
296
+ let products;
297
+ let ids;
298
+ if (isIncremental) {
299
+ const idSet = new Set(targetIds);
300
+ products = this.getProductsRef().filter((p) => idSet.has(p.id));
301
+ ids = products.map((p) => p.id);
302
+ } else {
303
+ products = this.getProductsRef();
304
+ const tIds = performance.now();
305
+ ids = new Array(products.length);
306
+ for (let i = 0; i < products.length; i++) {
307
+ ids[i] = products[i].id;
308
+ }
309
+ (0, import_product.perfMark)("prepareProducts.extractIds", performance.now() - tIds, { count: ids.length });
237
310
  }
238
- (0, import_product.perfMark)("prepareProducts.extractIds", performance.now() - tIds, { count: ids.length });
311
+ this.logInfo("获取到商品列表", { productCount: products.length });
239
312
  const tPrice = performance.now();
240
313
  const priceData = await this.loadProductsPrice({
241
314
  ids,
@@ -249,23 +322,27 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
249
322
  ...extraContext
250
323
  };
251
324
  const tFormat = performance.now();
252
- const processedProducts = await this.applyFormatters(allProducts, context);
325
+ const processedProducts = await this.applyFormatters(products, context);
253
326
  (0, import_product.perfMark)("prepareProducts.applyFormatters", performance.now() - tFormat, {
254
- count: allProducts.length,
327
+ count: products.length,
255
328
  formatterCount: this.formatters.length
256
329
  });
257
330
  this.logInfo("prepareProductsWithPrice 处理完成", {
258
- originalProductCount: allProducts.length,
331
+ mode: isIncremental ? "incremental" : "full",
332
+ originalProductCount: products.length,
259
333
  processedProductCount: processedProducts.length,
260
334
  formatterCount: this.formatters.length
261
335
  });
262
- await this.core.effects.emit(
263
- import_types.ProductsHooks.onProductsPriceApplied,
264
- processedProducts
265
- );
336
+ if (!isIncremental) {
337
+ await this.core.effects.emit(
338
+ import_types.ProductsHooks.onProductsPriceApplied,
339
+ processedProducts
340
+ );
341
+ }
266
342
  (0, import_product.perfMark)("prepareProductsWithPrice", performance.now() - tTotal, {
267
- productCount: allProducts.length,
268
- formatterCount: this.formatters.length
343
+ productCount: products.length,
344
+ formatterCount: this.formatters.length,
345
+ mode: isIncremental ? "incremental" : "full"
269
346
  });
270
347
  return processedProducts;
271
348
  } catch (err) {
@@ -604,14 +681,6 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
604
681
  (0, import_product.perfMark)("refreshProducts", performance.now() - tTotal, { count: this.store.list.length });
605
682
  return this.store.list;
606
683
  }
607
- /**
608
- * 指定商品的报价单价格变更时,清除价格缓存
609
- * 后续查询会走完整的 formatter 管道重建缓存
610
- */
611
- async updateProductPriceByIds(ids) {
612
- this.logInfo("updateProductPriceByIds: 清除价格缓存", { ids });
613
- this.clearPriceCache();
614
- }
615
684
  /**
616
685
  * 清空缓存
617
686
  */
@@ -838,17 +907,21 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
838
907
  *
839
908
  * product 模块:
840
909
  * - operation === 'delete' → 本地删除
841
- * - change_types 包含 price → SSE 增量拉取
910
+ * - change_types 包含 price → 仅收集变更 IDs(不拉商品数据)
842
911
  * - 有 body → body 完整数据直接覆盖本地
912
+ * - 其他 → SSE 增量拉取
843
913
  *
844
- * product_collection / product_category / product_quotation
914
+ * product_collection / product_category:
845
915
  * - 按 relation_product_ids SSE 拉取受影响商品
846
916
  *
847
- * 处理完成后清除价格缓存并 emit onProductsSyncCompleted,
848
- * Server 层监听该事件后重新查询,走完整的 formatter 管道重建缓存
917
+ * product_quotation:
918
+ * - 报价单变更影响范围大,直接清除价格缓存走全量重建
919
+ *
920
+ * 处理完成后 emit onProductsSyncCompleted(携带 changedIds),
921
+ * Server 层监听该事件后对变更商品增量执行 prepareProductsWithPrice 并更新缓存
849
922
  */
850
923
  async processProductSyncMessages() {
851
- var _a, _b, _c, _d, _e;
924
+ var _a, _b, _c, _d, _e, _f;
852
925
  const messages = [...this.pendingSyncMessages];
853
926
  this.pendingSyncMessages = [];
854
927
  if (messages.length === 0)
@@ -857,28 +930,30 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
857
930
  const deleteIds = [];
858
931
  const bodyUpdates = /* @__PURE__ */ new Map();
859
932
  const sseRefreshIds = [];
933
+ const priceRefreshIds = [];
934
+ let shouldClearPriceCache = false;
860
935
  for (const msg of messages) {
861
936
  const channelKey = msg._channelKey || msg.module || "product";
862
937
  if (channelKey === "product") {
938
+ if ((_a = msg.relation_product_ids) == null ? void 0 : _a.length) {
939
+ sseRefreshIds.push(...msg.relation_product_ids);
940
+ }
863
941
  if (msg.operation === "delete" || msg.action === "delete") {
864
- if ((_a = msg.ids) == null ? void 0 : _a.length)
942
+ if ((_b = msg.ids) == null ? void 0 : _b.length)
865
943
  deleteIds.push(...msg.ids);
866
944
  else if (msg.id)
867
945
  deleteIds.push(msg.id);
868
946
  continue;
869
947
  }
870
- if ((_b = msg.change_types) == null ? void 0 : _b.includes("price")) {
948
+ if ((_c = msg.change_types) == null ? void 0 : _c.includes("price")) {
871
949
  const ids = msg.ids || (msg.id ? [msg.id] : []);
872
- sseRefreshIds.push(...ids);
950
+ priceRefreshIds.push(...ids);
873
951
  continue;
874
952
  }
875
953
  if (msg.body) {
876
954
  const bodyId = msg.body.id || msg.id;
877
955
  if (bodyId)
878
956
  bodyUpdates.set(bodyId, msg.body);
879
- if ((_c = msg.relation_product_ids) == null ? void 0 : _c.length) {
880
- sseRefreshIds.push(...msg.relation_product_ids);
881
- }
882
957
  continue;
883
958
  }
884
959
  if ((_d = msg.ids) == null ? void 0 : _d.length) {
@@ -886,14 +961,20 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
886
961
  } else if (msg.id) {
887
962
  sseRefreshIds.push(msg.id);
888
963
  }
889
- } else if (["product_collection", "product_category", "product_quotation"].includes(channelKey)) {
964
+ } else if (channelKey === "product_quotation") {
965
+ shouldClearPriceCache = true;
890
966
  if ((_e = msg.relation_product_ids) == null ? void 0 : _e.length) {
891
967
  sseRefreshIds.push(...msg.relation_product_ids);
892
968
  }
969
+ } else if (["product_collection", "product_category"].includes(channelKey)) {
970
+ if ((_f = msg.relation_product_ids) == null ? void 0 : _f.length) {
971
+ sseRefreshIds.push(...msg.relation_product_ids);
972
+ }
893
973
  }
894
974
  }
895
975
  const uniqueDeleteIds = [...new Set(deleteIds)];
896
976
  const uniqueSSEIds = [...new Set(sseRefreshIds)];
977
+ const uniquePriceIds = [...new Set(priceRefreshIds)];
897
978
  if (uniqueDeleteIds.length > 0) {
898
979
  await this.removeProductsByIds(uniqueDeleteIds);
899
980
  }
@@ -910,17 +991,30 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
910
991
  receivedCount: freshProducts.length
911
992
  });
912
993
  }
994
+ const allChangedIds = [.../* @__PURE__ */ new Set([
995
+ ...Array.from(bodyUpdates.keys()),
996
+ ...uniqueSSEIds,
997
+ ...uniquePriceIds
998
+ ])];
913
999
  this.logInfo("processProductSyncMessages: 处理完成", {
914
1000
  deleteCount: uniqueDeleteIds.length,
915
1001
  bodyUpdateCount: bodyUpdates.size,
916
- sseRefreshCount: uniqueSSEIds.length
1002
+ sseRefreshCount: uniqueSSEIds.length,
1003
+ priceRefreshCount: uniquePriceIds.length,
1004
+ allChangedIdsCount: allChangedIds.length,
1005
+ shouldClearPriceCache
917
1006
  });
918
- if (uniqueDeleteIds.length === 0 && bodyUpdates.size === 0 && uniqueSSEIds.length === 0) {
1007
+ const hasChanges = uniqueDeleteIds.length > 0 || allChangedIds.length > 0 || shouldClearPriceCache;
1008
+ if (!hasChanges) {
919
1009
  this.logInfo("processProductSyncMessages: 没有变更,不触发 onProductsSyncCompleted");
920
1010
  return;
921
1011
  }
922
- this.clearPriceCache();
923
- await this.core.effects.emit(import_types.ProductsHooks.onProductsSyncCompleted, null);
1012
+ if (shouldClearPriceCache) {
1013
+ this.clearPriceCache();
1014
+ }
1015
+ await this.core.effects.emit(import_types.ProductsHooks.onProductsSyncCompleted, {
1016
+ changedIds: allChangedIds
1017
+ });
924
1018
  }
925
1019
  /**
926
1020
  * 通过 SSE 按 ids 增量拉取商品数据
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "private": false,
3
3
  "name": "@pisell/pisellos",
4
- "version": "2.2.88",
4
+ "version": "2.2.90",
5
5
  "description": "一个可扩展的前端模块化SDK框架,支持插件系统",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",