@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.
@@ -77,7 +77,30 @@ export declare enum ProductsHooks {
77
77
  onProductsLoaded = "products:onProductsLoaded",
78
78
  onProductsChanged = "products:onProductsChanged",
79
79
  onProductSelected = "products:onProductSelected",
80
- onProductsPriceApplied = "products:onProductsPriceApplied"
80
+ onProductsPriceApplied = "products:onProductsPriceApplied",
81
+ /** pubsub 同步批次处理完成后触发,Server 层监听此事件重新计算并推送 */
82
+ onProductsSyncCompleted = "products:onProductsSyncCompleted"
83
+ }
84
+ /** pubsub 商品同步消息结构 */
85
+ export interface ProductSyncMessage {
86
+ shop_id?: number;
87
+ module?: string;
88
+ action?: string;
89
+ id?: number;
90
+ /** 批量操作的商品 ID 列表 */
91
+ ids?: number[];
92
+ /** 完整商品数据(普通字段修改时携带) */
93
+ body?: any;
94
+ /** 操作类型(如 "delete" 表示删除) */
95
+ operation?: string;
96
+ /** 变更字段类型(如 ["price"]、["stock"]) */
97
+ change_types?: string[];
98
+ /** 关联商品 ID(product_collection / product_category / product_quotation 变更时携带) */
99
+ relation_product_ids?: number[];
100
+ message_uuid?: string;
101
+ timestamp?: string;
102
+ /** 内部标记:来源频道 key */
103
+ _channelKey?: string;
81
104
  }
82
105
  /**
83
106
  * 商品格式化器上下文
@@ -30,9 +30,12 @@ export var ProductsHooks = /*#__PURE__*/function (ProductsHooks) {
30
30
  ProductsHooks["onProductsChanged"] = "products:onProductsChanged";
31
31
  ProductsHooks["onProductSelected"] = "products:onProductSelected";
32
32
  ProductsHooks["onProductsPriceApplied"] = "products:onProductsPriceApplied";
33
+ ProductsHooks["onProductsSyncCompleted"] = "products:onProductsSyncCompleted";
33
34
  return ProductsHooks;
34
35
  }({});
35
36
 
37
+ /** pubsub 商品同步消息结构 */
38
+
36
39
  /**
37
40
  * 商品格式化器上下文
38
41
  */
@@ -1,5 +1,6 @@
1
1
  import { ProductData } from '../../modules/Product/types';
2
2
  import { FormattedProductData, LoadProductsPriceData, ProductFormatterContext } from '../modules/products/types';
3
+ export declare function perfMark(label: string, durationMs: number, meta?: Record<string, any>): void;
3
4
  /**
4
5
  * 将价格数据应用到商品列表(高性能版本)
5
6
  * 通过预构建 Map 索引,将时间复杂度从 O(n×m) 优化到 O(n+m)
@@ -9,6 +9,20 @@ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol"
9
9
  function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
10
10
  import { getScheduleStartEndTimePoints } from "../modules/schedule/utils";
11
11
  import { getUniqueId } from "../../modules/Cart/utils";
12
+ export function perfMark(label, durationMs, meta) {
13
+ try {
14
+ var w = typeof window !== 'undefined' ? window : typeof globalThis !== 'undefined' ? globalThis : null;
15
+ if (!w) return;
16
+ if (!w.__PERF__) w.__PERF__ = {
17
+ records: []
18
+ };
19
+ w.__PERF__.records.push(_objectSpread({
20
+ label: label,
21
+ duration: Math.round(durationMs * 100) / 100,
22
+ ts: Date.now()
23
+ }, meta));
24
+ } catch (_unused) {/* noop */}
25
+ }
12
26
 
13
27
  /**
14
28
  * 构建价格数据的 Map 索引结构
@@ -39,7 +53,7 @@ function buildPriceIndexMap(priceData) {
39
53
  if (p.bundle_group && p.bundle_group.length > 0) {
40
54
  var groupMap = new Map();
41
55
  p.bundle_group.forEach(function (bg) {
42
- var itemMap = new Map(bg.bundle_item.map(function (bi) {
56
+ var itemMap = new Map((bg.bundle_item || []).map(function (bi) {
43
57
  return [bi.id, bi];
44
58
  }));
45
59
  groupMap.set(bg.id, itemMap);
@@ -62,40 +76,33 @@ function buildPriceIndexMap(priceData) {
62
76
  * @returns 应用价格后的商品列表
63
77
  */
64
78
  export function applyPriceDataToProducts(products, priceData) {
65
- // 如果没有价格数据,直接返回原商品
79
+ var t0 = performance.now();
66
80
  if (!priceData || priceData.length === 0) {
67
81
  console.log('[applyPriceDataToProducts] 没有价格数据,返回原商品');
68
82
  return products;
69
83
  }
70
-
71
- // 预先构建 Map 索引(O(m) 时间)
84
+ var t1 = performance.now();
72
85
  var _buildPriceIndexMap = buildPriceIndexMap(priceData),
73
86
  priceMap = _buildPriceIndexMap.priceMap,
74
87
  variantMap = _buildPriceIndexMap.variantMap,
75
88
  bundleMap = _buildPriceIndexMap.bundleMap;
76
- console.log("[applyPriceDataToProducts] \u5DF2\u6784\u5EFA\u4EF7\u683C\u7D22\u5F15\uFF0C\u5171 ".concat(priceMap.size, " \u4E2A\u5546\u54C1\u4EF7\u683C"));
77
-
78
- // 应用价格到商品(O(n) 时间)
89
+ perfMark('buildPriceIndexMap', performance.now() - t1, {
90
+ priceCount: priceData.length
91
+ });
92
+ var t2 = performance.now();
79
93
  var updatedProducts = products.map(function (product) {
80
- // O(1) 查询商品价格
81
94
  var priceInfo = priceMap.get(product.id);
82
95
  if (!priceInfo) {
83
96
  return product;
84
97
  }
85
-
86
- // 深拷贝商品对象
87
98
  var updatedProduct = _objectSpread({}, product);
88
-
89
- // 1. 更新主价格(转换为 string 类型)
90
99
  updatedProduct.price = priceInfo.price;
91
100
  updatedProduct.base_price = priceInfo.base_price;
92
-
93
- // 2. 更新 variant 价格
94
101
  if (updatedProduct.variant && updatedProduct.variant.length > 0) {
95
102
  var productVariantMap = variantMap.get(product.id);
96
103
  if (productVariantMap) {
97
104
  updatedProduct.variant = updatedProduct.variant.map(function (v) {
98
- var priceVariant = productVariantMap.get(v.id); // O(1)
105
+ var priceVariant = productVariantMap.get(v.id);
99
106
  if (priceVariant) {
100
107
  return _objectSpread(_objectSpread({}, v), {}, {
101
108
  price: String(priceVariant.price),
@@ -106,19 +113,16 @@ export function applyPriceDataToProducts(products, priceData) {
106
113
  });
107
114
  }
108
115
  }
109
-
110
- // 3. 更新 bundle_group 价格
111
116
  if (updatedProduct.bundle_group && updatedProduct.bundle_group.length > 0) {
112
117
  var productBundleMap = bundleMap.get(product.id);
113
118
  if (productBundleMap) {
114
119
  updatedProduct.bundle_group = updatedProduct.bundle_group.map(function (bg) {
115
- var groupItemMap = productBundleMap.get(bg.id); // O(1)
120
+ var groupItemMap = productBundleMap.get(bg.id);
116
121
  if (!groupItemMap) return bg;
117
122
  return _objectSpread(_objectSpread({}, bg), {}, {
118
123
  bundle_item: bg.bundle_item.map(function (bi) {
119
- var priceBi = groupItemMap.get(bi.id); // O(1)
124
+ var priceBi = groupItemMap.get(bi.id);
120
125
  if (priceBi) {
121
- // 处理 markdown 类型
122
126
  var price = priceBi.price;
123
127
  return _objectSpread(_objectSpread({}, bi), {}, {
124
128
  price: String(price),
@@ -133,7 +137,13 @@ export function applyPriceDataToProducts(products, priceData) {
133
137
  }
134
138
  return updatedProduct;
135
139
  });
136
- console.log("[applyPriceDataToProducts] \u5DF2\u5E94\u7528\u4EF7\u683C\u5230 ".concat(updatedProducts.length, " \u4E2A\u5546\u54C1"));
140
+ perfMark('applyPriceDataToProducts.mapProducts', performance.now() - t2, {
141
+ count: products.length
142
+ });
143
+ perfMark('applyPriceDataToProducts', performance.now() - t0, {
144
+ productCount: products.length,
145
+ priceCount: priceData.length
146
+ });
137
147
  return updatedProducts;
138
148
  }
139
149
  export var getIsSessionProduct = function getIsSessionProduct(product) {
@@ -314,22 +324,22 @@ var genCartDetailValue = function genCartDetailValue(product, scheduleTimeSlots)
314
324
  * @returns 应用详情值后的商品列表
315
325
  */
316
326
  export function applyDetailValueToProducts(products, context) {
327
+ var t0 = performance.now();
317
328
  var newProducts = products.map(function (product) {
318
- /** 是否打开详情弹窗 套餐组、选项组、组合规格、日程商品、日程时间段大于1个 打开详情弹窗 */
319
329
  var _getIsOpenDetailModal = getIsOpenDetailModal(product, context),
320
330
  isOpenDetailModal = _getIsOpenDetailModal.isOpenDetailModal,
321
331
  scheduleTimeSlots = _getIsOpenDetailModal.scheduleTimeSlots;
322
332
  var cartDetailValue = null;
323
333
  if (!isOpenDetailModal) {
324
- // 生成购物车详情值
325
334
  cartDetailValue = genCartDetailValue(product, scheduleTimeSlots);
326
335
  }
327
336
  return _objectSpread(_objectSpread({}, product), {}, {
328
- // 是否打开详情弹窗
329
337
  isOpenDetailModal: isOpenDetailModal,
330
- // 购物车详情值
331
338
  cartDetailValue: cartDetailValue
332
339
  });
333
340
  });
341
+ perfMark('applyDetailValueToProducts', performance.now() - t0, {
342
+ count: products.length
343
+ });
334
344
  return newProducts;
335
345
  }
@@ -21,7 +21,15 @@ export declare class BookingTicketImpl extends BaseModule implements Module {
21
21
  * @param params 包含 schedule_date 的参数
22
22
  * @returns 商品列表
23
23
  */
24
- loadProducts(params?: ILoadProductsParams): Promise<any>;
24
+ loadProducts(params?: ILoadProductsParams, options?: {
25
+ callback?: (result: any) => void;
26
+ subscriberId?: string;
27
+ }): Promise<any>;
28
+ /**
29
+ * 取消商品查询订阅
30
+ * @param subscriberId 订阅时传入的 subscriberId
31
+ */
32
+ unsubscribeProductQuery(subscriberId?: string): void;
25
33
  /**
26
34
  * 初始化外设扫码结果监听
27
35
  */
@@ -131,39 +131,41 @@ export var BookingTicketImpl = /*#__PURE__*/function (_BaseModule) {
131
131
  value: (function () {
132
132
  var _loadProducts = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee2() {
133
133
  var params,
134
+ options,
134
135
  schedule_date,
135
136
  customer_id,
136
137
  menu_list_ids,
137
138
  schedule_datetime,
138
- result,
139
+ _result,
139
140
  _args2 = arguments;
140
141
  return _regeneratorRuntime().wrap(function _callee2$(_context2) {
141
142
  while (1) switch (_context2.prev = _context2.next) {
142
143
  case 0:
143
144
  params = _args2.length > 0 && _args2[0] !== undefined ? _args2[0] : {};
145
+ options = _args2.length > 1 ? _args2[1] : undefined;
144
146
  schedule_date = params.schedule_date, customer_id = params.customer_id, menu_list_ids = params.menu_list_ids, schedule_datetime = params.schedule_datetime;
145
- _context2.prev = 2;
146
- _context2.next = 5;
147
+ _context2.prev = 3;
148
+ _context2.next = 6;
147
149
  return this.store.products.loadProducts(_objectSpread(_objectSpread({
148
150
  with_count: ['bundleGroup', 'optionGroup'],
149
151
  with_schedule: 1
150
152
  }, params), {}, {
151
153
  cacheId: this.cacheId
152
- }));
153
- case 5:
154
- result = _context2.sent;
155
- this.core.effects.emit("".concat(this.name, ":onProductsLoaded"), result);
156
- return _context2.abrupt("return", result);
157
- case 10:
158
- _context2.prev = 10;
159
- _context2.t0 = _context2["catch"](2);
154
+ }), options);
155
+ case 6:
156
+ _result = _context2.sent;
157
+ this.core.effects.emit("".concat(this.name, ":onProductsLoaded"), _result);
158
+ return _context2.abrupt("return", _result);
159
+ case 11:
160
+ _context2.prev = 11;
161
+ _context2.t0 = _context2["catch"](3);
160
162
  console.error('Failed to load products:', _context2.t0);
161
163
  throw _context2.t0;
162
- case 14:
164
+ case 15:
163
165
  case "end":
164
166
  return _context2.stop();
165
167
  }
166
- }, _callee2, this, [[2, 10]]);
168
+ }, _callee2, this, [[3, 11]]);
167
169
  }));
168
170
  function loadProducts() {
169
171
  return _loadProducts.apply(this, arguments);
@@ -171,9 +173,20 @@ export var BookingTicketImpl = /*#__PURE__*/function (_BaseModule) {
171
173
  return loadProducts;
172
174
  }()
173
175
  /**
174
- * 初始化外设扫码结果监听
176
+ * 取消商品查询订阅
177
+ * @param subscriberId 订阅时传入的 subscriberId
175
178
  */
176
179
  )
180
+ }, {
181
+ key: "unsubscribeProductQuery",
182
+ value: function unsubscribeProductQuery(subscriberId) {
183
+ var _this$core$server;
184
+ (_this$core$server = this.core.server) === null || _this$core$server === void 0 || _this$core$server.removeProductQuerySubscriber(subscriberId);
185
+ }
186
+
187
+ /**
188
+ * 初始化外设扫码结果监听
189
+ */
177
190
  }, {
178
191
  key: "initPeripheralsListener",
179
192
  value: function initPeripheralsListener() {
@@ -248,7 +261,7 @@ export var BookingTicketImpl = /*#__PURE__*/function (_BaseModule) {
248
261
  value: (function () {
249
262
  var _getCustomerList = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee5() {
250
263
  var params,
251
- result,
264
+ _result2,
252
265
  _args5 = arguments;
253
266
  return _regeneratorRuntime().wrap(function _callee5$(_context5) {
254
267
  while (1) switch (_context5.prev = _context5.next) {
@@ -258,8 +271,8 @@ export var BookingTicketImpl = /*#__PURE__*/function (_BaseModule) {
258
271
  _context5.next = 4;
259
272
  return this.store.customer.getCustomerList(params);
260
273
  case 4:
261
- result = _context5.sent;
262
- return _context5.abrupt("return", result);
274
+ _result2 = _context5.sent;
275
+ return _context5.abrupt("return", _result2);
263
276
  case 8:
264
277
  _context5.prev = 8;
265
278
  _context5.t0 = _context5["catch"](1);
@@ -397,7 +410,7 @@ export var BookingTicketImpl = /*#__PURE__*/function (_BaseModule) {
397
410
  key: "changeCustomerPage",
398
411
  value: (function () {
399
412
  var _changeCustomerPage = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee6(page, pageSize) {
400
- var result;
413
+ var _result3;
401
414
  return _regeneratorRuntime().wrap(function _callee6$(_context6) {
402
415
  while (1) switch (_context6.prev = _context6.next) {
403
416
  case 0:
@@ -405,8 +418,8 @@ export var BookingTicketImpl = /*#__PURE__*/function (_BaseModule) {
405
418
  _context6.next = 3;
406
419
  return this.store.customer.changeCustomerPage(page, pageSize);
407
420
  case 3:
408
- result = _context6.sent;
409
- return _context6.abrupt("return", result);
421
+ _result3 = _context6.sent;
422
+ return _context6.abrupt("return", _result3);
410
423
  case 7:
411
424
  _context6.prev = 7;
412
425
  _context6.t0 = _context6["catch"](0);
@@ -432,7 +445,7 @@ export var BookingTicketImpl = /*#__PURE__*/function (_BaseModule) {
432
445
  key: "loadMoreCustomers",
433
446
  value: (function () {
434
447
  var _loadMoreCustomers = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee7() {
435
- var result;
448
+ var _result4;
436
449
  return _regeneratorRuntime().wrap(function _callee7$(_context7) {
437
450
  while (1) switch (_context7.prev = _context7.next) {
438
451
  case 0:
@@ -440,9 +453,9 @@ export var BookingTicketImpl = /*#__PURE__*/function (_BaseModule) {
440
453
  _context7.next = 3;
441
454
  return this.store.customer.loadMoreCustomers();
442
455
  case 3:
443
- result = _context7.sent;
444
- this.core.effects.emit("".concat(this.name, ":onCustomerListUpdate"), result);
445
- return _context7.abrupt("return", result);
456
+ _result4 = _context7.sent;
457
+ this.core.effects.emit("".concat(this.name, ":onCustomerListUpdate"), _result4);
458
+ return _context7.abrupt("return", _result4);
446
459
  case 8:
447
460
  _context7.prev = 8;
448
461
  _context7.t0 = _context7["catch"](0);
@@ -470,7 +483,7 @@ export var BookingTicketImpl = /*#__PURE__*/function (_BaseModule) {
470
483
  value: (function () {
471
484
  var _resetAndLoadCustomers = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee8() {
472
485
  var params,
473
- result,
486
+ _result5,
474
487
  _args8 = arguments;
475
488
  return _regeneratorRuntime().wrap(function _callee8$(_context8) {
476
489
  while (1) switch (_context8.prev = _context8.next) {
@@ -480,9 +493,9 @@ export var BookingTicketImpl = /*#__PURE__*/function (_BaseModule) {
480
493
  _context8.next = 4;
481
494
  return this.store.customer.resetAndLoadCustomers(params);
482
495
  case 4:
483
- result = _context8.sent;
484
- this.core.effects.emit("".concat(this.name, ":onCustomerListReset"), result);
485
- return _context8.abrupt("return", result);
496
+ _result5 = _context8.sent;
497
+ this.core.effects.emit("".concat(this.name, ":onCustomerListReset"), _result5);
498
+ return _context8.abrupt("return", _result5);
486
499
  case 9:
487
500
  _context8.prev = 9;
488
501
  _context8.t0 = _context8["catch"](1);
@@ -50,6 +50,8 @@ export interface PisellCore {
50
50
  };
51
51
  context: BusinessContext;
52
52
  validateContext: (config: ModuleContextConfig) => boolean;
53
+ serverOptions?: ServerOptions;
54
+ server?: any;
53
55
  }
54
56
  /**
55
57
  * 业务上下文接口
@@ -90,6 +92,7 @@ export interface ServerModuleConfig {
90
92
  * Server 配置选项
91
93
  */
92
94
  export interface ServerOptions {
95
+ All_DATA_SOURCES: Record<string, any>;
93
96
  /** 要启用的模块列表 */
94
97
  modules?: string[] | ServerModuleConfig[];
95
98
  /** 是否自动初始化数据 */
@@ -1,4 +1,4 @@
1
- import { Plugin, PluginOptions, Module, ModuleOptions, PisellCore, PisellOSOptions, BusinessContext, ModuleContextConfig, InitializeServerOptions } from '../types';
1
+ import { Plugin, PluginOptions, Module, ModuleOptions, PisellCore, PisellOSOptions, BusinessContext, ModuleContextConfig, ServerOptions, InitializeServerOptions } from '../types';
2
2
  import { EffectsManager } from '../effects';
3
3
  /**
4
4
  * pisell OS 核心实现
@@ -11,7 +11,7 @@ declare class PisellOSCore implements PisellCore {
11
11
  private debug;
12
12
  context: BusinessContext;
13
13
  server: any;
14
- private serverOptions?;
14
+ serverOptions?: ServerOptions;
15
15
  constructor(options?: PisellOSOptions);
16
16
  private initialize;
17
17
  /**
@@ -22,6 +22,9 @@ export declare class ProductList extends BaseModule implements Module {
22
22
  schedule_datetime?: string;
23
23
  with_count?: string[];
24
24
  with_schedule?: number;
25
+ }, options?: {
26
+ callback?: (result: any) => void;
27
+ subscriberId?: string;
25
28
  }): Promise<any>;
26
29
  loadProductsPrice({ ids, customer_id, schedule_date, channel, }: {
27
30
  ids?: number[];
@@ -60,7 +60,7 @@ var ProductList = class extends import_BaseModule.BaseModule {
60
60
  schedule_date,
61
61
  cacheId,
62
62
  with_schedule
63
- }) {
63
+ }, options) {
64
64
  var _a, _b;
65
65
  let userPlugin = this.core.getPlugin("user");
66
66
  let customer_id = void 0;
@@ -100,7 +100,7 @@ var ProductList = class extends import_BaseModule.BaseModule {
100
100
  with_schedule,
101
101
  schedule_datetime
102
102
  },
103
- { osServer: true }
103
+ { osServer: true, callback: options == null ? void 0 : options.callback, subscriberId: options == null ? void 0 : options.subscriberId }
104
104
  );
105
105
  const sortedList = (productsData.data.list || []).slice().sort((a, b) => Number(b.sort) - Number(a.sort));
106
106
  this.addProduct(sortedList);
@@ -69,6 +69,8 @@ export interface RequestOptions {
69
69
  customToast?: () => void;
70
70
  cache?: CacheProps;
71
71
  osServer?: boolean;
72
+ callback?: (res: any) => void;
73
+ subscriberId?: string;
72
74
  }
73
75
  /**
74
76
  * 响应接口
@@ -18,6 +18,7 @@ declare class Server {
18
18
  quotation?: QuotationModule;
19
19
  schedule?: ScheduleModuleEx;
20
20
  router: Router;
21
+ private productQuerySubscribers;
21
22
  private moduleRegistry;
22
23
  constructor(core: PisellCore);
23
24
  /**
@@ -85,6 +86,12 @@ declare class Server {
85
86
  * 获取所有已注册的模块列表
86
87
  */
87
88
  getRegisteredModules(): string[];
89
+ /**
90
+ * 后台静默刷新商品数据
91
+ * 重新拉取全量 SSE 接口,更新本地数据后触发 onProductsSyncCompleted
92
+ * 不影响当前界面展示,适用于切回前台、定时刷新等场景
93
+ */
94
+ refreshProductsInBackground(): Promise<void>;
88
95
  /**
89
96
  * 清空所有server模块的IndexedDB缓存
90
97
  * @returns Promise<void>
@@ -132,10 +139,28 @@ declare class Server {
132
139
  */
133
140
  private registerServerRoutes;
134
141
  /**
135
- * 处理商品查询请求(编排 Products、Menu、Schedule 模块)
136
- * 这是一个业务编排方法,协调多个模块完成复杂的业务需求
142
+ * 根据 subscriberId 移除商品查询订阅者
143
+ */
144
+ removeProductQuerySubscriber(subscriberId?: string): void;
145
+ /**
146
+ * 处理商品查询请求
147
+ * 存储订阅者信息,便于数据变更时推送最新结果
137
148
  */
138
149
  private handleProductQuery;
150
+ /**
151
+ * 取消商品查询订阅(HTTP 路由入口)
152
+ */
153
+ private handleUnsubscribeProductQuery;
154
+ /**
155
+ * 商品查询的核心计算逻辑(编排 Products、Menu、Schedule 模块)
156
+ * 供 handleProductQuery 首次返回及 pubsub 变更推送复用
157
+ */
158
+ private computeProductQueryResult;
159
+ /**
160
+ * 数据变更后,遍历所有订阅者重新计算查询结果并通过 callback 推送
161
+ * 由 ProductsModule 的 onProductsSyncCompleted 事件触发
162
+ */
163
+ private recomputeAndNotifyProductQuery;
139
164
  /**
140
165
  * 根据餐牌配置过滤商品
141
166
  * @param products 所有商品列表