@pisell/pisellos 2.3.27 → 2.3.29

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 (83) hide show
  1. package/dist/model/strategy/adapter/dataVariant/adapter.d.ts +50 -0
  2. package/dist/model/strategy/adapter/dataVariant/adapter.js +167 -0
  3. package/dist/model/strategy/adapter/dataVariant/evaluator.d.ts +89 -0
  4. package/dist/model/strategy/adapter/dataVariant/evaluator.js +780 -0
  5. package/dist/model/strategy/adapter/dataVariant/examples.d.ts +39 -0
  6. package/dist/model/strategy/adapter/dataVariant/examples.js +277 -0
  7. package/dist/model/strategy/adapter/dataVariant/index.d.ts +4 -0
  8. package/dist/model/strategy/adapter/dataVariant/index.js +4 -0
  9. package/dist/model/strategy/adapter/dataVariant/type.d.ts +148 -0
  10. package/dist/model/strategy/adapter/dataVariant/type.js +54 -0
  11. package/dist/model/strategy/adapter/index.d.ts +4 -0
  12. package/dist/model/strategy/adapter/index.js +5 -1
  13. package/dist/modules/Discount/index.d.ts +5 -2
  14. package/dist/modules/Discount/index.js +30 -7
  15. package/dist/modules/Discount/types.d.ts +4 -0
  16. package/dist/modules/Order/index.d.ts +7 -1
  17. package/dist/modules/Order/index.js +187 -74
  18. package/dist/modules/Order/types.d.ts +2 -0
  19. package/dist/modules/Order/utils/orderCollectionIdentity.d.ts +53 -0
  20. package/dist/modules/Order/utils/orderCollectionIdentity.js +405 -0
  21. package/dist/modules/Order/utils.d.ts +2 -1
  22. package/dist/modules/Order/utils.js +2 -8
  23. package/dist/modules/Product/types.d.ts +22 -0
  24. package/dist/modules/ProductList/index.d.ts +1 -1
  25. package/dist/modules/ProductList/index.js +4 -2
  26. package/dist/modules/ProductList/types.d.ts +2 -0
  27. package/dist/server/index.d.ts +50 -0
  28. package/dist/server/index.js +995 -749
  29. package/dist/server/modules/order/index.d.ts +10 -4
  30. package/dist/server/modules/order/index.js +404 -399
  31. package/dist/server/modules/products/index.d.ts +26 -7
  32. package/dist/server/modules/products/index.js +167 -76
  33. package/dist/server/modules/products/types.d.ts +14 -0
  34. package/dist/solution/BaseSales/index.d.ts +9 -1
  35. package/dist/solution/BaseSales/index.js +998 -756
  36. package/dist/solution/BaseSales/utils/parseSalesResponse.d.ts +3 -3
  37. package/dist/solution/BaseSales/utils/parseSalesResponse.js +6 -8
  38. package/dist/solution/BookingByStep/index.d.ts +1 -1
  39. package/dist/solution/BookingTicket/index.d.ts +14 -2
  40. package/dist/solution/BookingTicket/types.d.ts +2 -0
  41. package/dist/solution/ShopDiscount/index.js +15 -14
  42. package/lib/model/strategy/adapter/dataVariant/adapter.d.ts +50 -0
  43. package/lib/model/strategy/adapter/dataVariant/adapter.js +149 -0
  44. package/lib/model/strategy/adapter/dataVariant/evaluator.d.ts +89 -0
  45. package/lib/model/strategy/adapter/dataVariant/evaluator.js +583 -0
  46. package/lib/model/strategy/adapter/dataVariant/examples.d.ts +39 -0
  47. package/lib/model/strategy/adapter/dataVariant/examples.js +293 -0
  48. package/lib/model/strategy/adapter/dataVariant/index.d.ts +4 -0
  49. package/lib/model/strategy/adapter/dataVariant/index.js +38 -0
  50. package/lib/model/strategy/adapter/dataVariant/type.d.ts +148 -0
  51. package/lib/model/strategy/adapter/dataVariant/type.js +31 -0
  52. package/lib/model/strategy/adapter/index.d.ts +4 -0
  53. package/lib/model/strategy/adapter/index.js +13 -2
  54. package/lib/modules/Discount/index.d.ts +5 -2
  55. package/lib/modules/Discount/index.js +23 -3
  56. package/lib/modules/Discount/types.d.ts +4 -0
  57. package/lib/modules/Order/index.d.ts +7 -1
  58. package/lib/modules/Order/index.js +104 -6
  59. package/lib/modules/Order/types.d.ts +2 -0
  60. package/lib/modules/Order/utils/orderCollectionIdentity.d.ts +53 -0
  61. package/lib/modules/Order/utils/orderCollectionIdentity.js +415 -0
  62. package/lib/modules/Order/utils.d.ts +2 -1
  63. package/lib/modules/Order/utils.js +4 -11
  64. package/lib/modules/Product/types.d.ts +22 -0
  65. package/lib/modules/ProductList/index.d.ts +1 -1
  66. package/lib/modules/ProductList/index.js +4 -2
  67. package/lib/modules/ProductList/types.d.ts +2 -0
  68. package/lib/server/index.d.ts +50 -0
  69. package/lib/server/index.js +206 -29
  70. package/lib/server/modules/order/index.d.ts +10 -4
  71. package/lib/server/modules/order/index.js +199 -141
  72. package/lib/server/modules/products/index.d.ts +26 -7
  73. package/lib/server/modules/products/index.js +114 -35
  74. package/lib/server/modules/products/types.d.ts +14 -0
  75. package/lib/solution/BaseSales/index.d.ts +9 -1
  76. package/lib/solution/BaseSales/index.js +229 -54
  77. package/lib/solution/BaseSales/utils/parseSalesResponse.d.ts +3 -3
  78. package/lib/solution/BaseSales/utils/parseSalesResponse.js +4 -5
  79. package/lib/solution/BookingByStep/index.d.ts +1 -1
  80. package/lib/solution/BookingTicket/index.d.ts +14 -2
  81. package/lib/solution/BookingTicket/types.d.ts +2 -0
  82. package/lib/solution/ShopDiscount/index.js +2 -1
  83. package/package.json +1 -1
@@ -3,6 +3,7 @@ import type { CartItem } from "../Cart";
3
3
  import type { OrderProductDiscountItem, OrderProduct, OrderSubmitPayload, OrderSubmitProduct, OrderSummary, OrderTempOrder } from './types';
4
4
  import type { RulesParamsHooks } from '../Rules/types';
5
5
  import type { MapOrderPaymentsOptions, OrderPaymentData, OrderPaymentSource } from './types';
6
+ import { createUuidV4 } from './utils/orderCollectionIdentity';
6
7
  export interface OrderProductsDepositProtocol {
7
8
  id: number;
8
9
  title: string;
@@ -177,7 +178,7 @@ export declare const mergeRelationForms: (relationForms: {
177
178
  form_record_ids: number[];
178
179
  }[];
179
180
  export declare const getAllDiscountList: (cartItem: CartItem) => any;
180
- export declare function createUuidV4(): string;
181
+ export { createUuidV4 };
181
182
  export declare function isTempOrder(data: any): data is OrderTempOrder;
182
183
  export declare function formatDateTime(date: Date): string;
183
184
  export declare function normalizeBookingIsAll(booking: Record<string, any>): 0 | 1;
@@ -37,7 +37,7 @@ __export(utils_exports, {
37
37
  createDefaultOrderRulesHooks: () => createDefaultOrderRulesHooks,
38
38
  createDefaultTempOrder: () => createDefaultTempOrder,
39
39
  createEmptySummary: () => import_utils2.createEmptySummary,
40
- createUuidV4: () => createUuidV4,
40
+ createUuidV4: () => import_orderCollectionIdentity.createUuidV4,
41
41
  ensureManualProductDiscountList: () => import_manualProductDiscount.ensureManualProductDiscountList,
42
42
  ensureProductSku: () => ensureProductSku,
43
43
  findManualProductDiscountItem: () => import_manualProductDiscount.findManualProductDiscountItem,
@@ -86,6 +86,7 @@ module.exports = __toCommonJS(utils_exports);
86
86
  var import_dayjs = __toESM(require("dayjs"));
87
87
  var import_decimal = __toESM(require("decimal.js"));
88
88
  var import_utils = require("../../solution/ScanOrder/utils");
89
+ var import_orderCollectionIdentity = require("./utils/orderCollectionIdentity");
89
90
  var import_utils2 = require("../../solution/ScanOrder/utils");
90
91
  var import_manualProductDiscount = require("./utils/manualProductDiscount");
91
92
  function composeLinePrice(params) {
@@ -641,14 +642,6 @@ var getAllDiscountList = (cartItem) => {
641
642
  });
642
643
  return discountList;
643
644
  };
644
- function createUuidV4() {
645
- const template = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx";
646
- return template.replace(/[xy]/g, (char) => {
647
- const randomValue = Math.floor(Math.random() * 16);
648
- const value = char === "x" ? randomValue : randomValue & 3 | 8;
649
- return value.toString(16);
650
- });
651
- }
652
645
  function isTempOrder(data) {
653
646
  if (!data || typeof data !== "object")
654
647
  return false;
@@ -938,7 +931,7 @@ function buildSubmitPayload(params) {
938
931
  enhance
939
932
  } = params;
940
933
  const scheduleDate = tempOrder.schedule_date || tempOrder.created_at || formatDateTime(now);
941
- const bookingUuid = createUuidV4();
934
+ const bookingUuid = (0, import_orderCollectionIdentity.createUuidV4)();
942
935
  const {
943
936
  _extend,
944
937
  relation_id: _relationId,
@@ -1086,7 +1079,7 @@ function formatV1Product(products) {
1086
1079
  note: String(product.note ?? ""),
1087
1080
  rowKey: product.product_id,
1088
1081
  session: null,
1089
- unique: createUuidV4()
1082
+ unique: (0, import_orderCollectionIdentity.createUuidV4)()
1090
1083
  };
1091
1084
  });
1092
1085
  }
@@ -9,6 +9,26 @@ export interface ProductCollection {
9
9
  /** 商品集合封面 */
10
10
  cover: string;
11
11
  }
12
+ /**
13
+ * 商品 Data Variant 记录,结构与后端 data_variant 表对齐。
14
+ */
15
+ export interface ProductDataVariant {
16
+ /** 变体记录 id */
17
+ id: number;
18
+ /** 店铺 id */
19
+ shop_id: number;
20
+ /** 所属模块,商品场景固定为 product */
21
+ module: string;
22
+ /** 模块数据 id,商品场景对应 product.id */
23
+ module_data_id: number;
24
+ /** 关联 data_variant_rule.id */
25
+ data_variant_rule_id: number;
26
+ /** 命中策略后覆盖到商品上的字段 */
27
+ data: Record<string, any>;
28
+ created_at: string | null;
29
+ updated_at: string | null;
30
+ deleted_at: string | null;
31
+ }
12
32
  /**
13
33
  * 商品基本信息接口
14
34
  */
@@ -214,6 +234,8 @@ export interface ProductData {
214
234
  bundle_group_count: number;
215
235
  /** 选项组数量 */
216
236
  option_group_count: number;
237
+ /** 智能定价变体列表,/product/query with dataVariants 返回 */
238
+ data_variants?: ProductDataVariant[];
217
239
  /** 服务时长 */
218
240
  service_times?: any;
219
241
  }
@@ -26,7 +26,7 @@ export declare class ProductList extends BaseModule implements Module {
26
26
  * const products = await productList.getAddTimeProducts({ schedule_date: '2026-06-16' });
27
27
  */
28
28
  getAddTimeProducts(params?: ProductListLoadProductsParams): Promise<any>;
29
- loadProducts({ category_ids, product_ids, collection, menu_list_ids, customer_id: paramsCustomerId, with_count, schedule_datetime, schedule_date, cacheId, with_schedule, extension_type, status, }?: ProductListLoadProductsParams, options?: {
29
+ loadProducts({ category_ids, product_ids, collection, menu_list_ids, customer_id: paramsCustomerId, with_count, schedule_datetime, schedule_date, cacheId, with_schedule, extension_type, status, strategy_context, }?: ProductListLoadProductsParams, options?: {
30
30
  callback?: (result: any) => void;
31
31
  subscriberId?: string;
32
32
  }): Promise<any>;
@@ -95,7 +95,8 @@ var ProductList = class extends import_BaseModule.BaseModule {
95
95
  cacheId,
96
96
  with_schedule,
97
97
  extension_type,
98
- status = "published"
98
+ status = "published",
99
+ strategy_context
99
100
  } = {}, options) {
100
101
  var _a, _b;
101
102
  let userPlugin = this.core.getPlugin("user");
@@ -135,7 +136,8 @@ var ProductList = class extends import_BaseModule.BaseModule {
135
136
  is_eject: 1,
136
137
  with_schedule,
137
138
  schedule_datetime,
138
- extension_type
139
+ extension_type,
140
+ strategy_context
139
141
  },
140
142
  { osServer: true, callback: options == null ? void 0 : options.callback, subscriberId: options == null ? void 0 : options.subscriberId, customToast: () => {
141
143
  } }
@@ -12,6 +12,8 @@ export interface ProductListLoadProductsParams {
12
12
  with_schedule?: number;
13
13
  extension_type?: string[];
14
14
  status?: string;
15
+ /** 智能定价条件上下文,仅影响策略命中,不参与商品集合筛选 */
16
+ strategy_context?: Record<string, any>;
15
17
  }
16
18
  export interface ProductListData {
17
19
  list: ProductData[];
@@ -47,6 +47,8 @@ declare class Server {
47
47
  private prefixRouterGet;
48
48
  router: Router;
49
49
  private productQuerySubscribers;
50
+ /** subscriberId → 智能定价变价时间点定时器句柄(与 subscriber 分离存储,便于 clear) */
51
+ private productQueryScheduleTimers;
50
52
  private orderQuerySubscribers;
51
53
  private bookingQuerySubscribers;
52
54
  private bookingRemoteQuerySubscribers;
@@ -210,6 +212,48 @@ declare class Server {
210
212
  * 根据 subscriberId 移除商品查询订阅者
211
213
  */
212
214
  removeProductQuerySubscriber(subscriberId?: string): void;
215
+ /**
216
+ * 构建商品 query 响应 data 段,附带智能定价 schedule_time_points 元数据。
217
+ */
218
+ private buildProductQueryResultPayload;
219
+ /**
220
+ * 是否启用商品 query 订阅的 schedule 时间点定时重算。
221
+ * strategy_context.enable_schedule_reload === false 时关闭。
222
+ */
223
+ private shouldScheduleProductQueryReload;
224
+ /**
225
+ * 清除指定 subscriber 的全部 schedule 定时器,防止重复 query 或 unsubscribe 后泄漏。
226
+ */
227
+ private clearSubscriberScheduleTimers;
228
+ /**
229
+ * 从 productQueryScheduleTimers 移除已触发的 timer 引用。
230
+ */
231
+ private removeScheduleTimerRef;
232
+ /**
233
+ * 计算 schedule 变价时间点到当前时刻的延迟(ms)。已过期返回 -1。
234
+ */
235
+ private computeScheduleTimePointDelayMs;
236
+ /**
237
+ * 定时重算前刷新 subscriber schedule 上下文:使用执行瞬间时间,禁止沿用首次 query 的 schedule_datetime。
238
+ */
239
+ private refreshSubscriberScheduleAtExecution;
240
+ /**
241
+ * 为 subscriber 注册智能定价 scheduleTimePoints 定时重算。
242
+ * 必须先 clearSubscriberScheduleTimers,再调用本方法。
243
+ */
244
+ private scheduleSubscriberTimePointReloads;
245
+ /**
246
+ * schedule 变价时间点定时器触发:刷新 schedule_datetime 后重算并 callback。
247
+ */
248
+ private onScheduleTimePointTimerFire;
249
+ /**
250
+ * 对单个商品 query 订阅者重算并推送(定时路径;不新建 schedule timer)。
251
+ */
252
+ private recomputeAndNotifySubscriber;
253
+ /**
254
+ * query 完成后为 subscriber 同步 schedule 定时器(先 clear 再 schedule)。
255
+ */
256
+ private syncProductQueryScheduleTimers;
213
257
  /**
214
258
  * 处理商品查询请求
215
259
  * 存储订阅者信息,便于数据变更时推送最新结果
@@ -570,6 +614,12 @@ declare class Server {
570
614
  * @param options.changedIds 变更的商品 IDs,用于增量更新价格缓存
571
615
  */
572
616
  private computeProductQueryResult;
617
+ /**
618
+ * 构建商品格式化上下文。
619
+ *
620
+ * 智能定价条件使用全量 menuList/scheduleList;Product Query 顶层参数只保留筛选商品集合所需字段。
621
+ */
622
+ private buildProductFormatterContext;
573
623
  /**
574
624
  * 数据变更后,遍历所有订阅者重新计算查询结果并通过 callback 推送
575
625
  * 由 ProductsModule 的 onProductsSyncCompleted 事件触发
@@ -76,6 +76,8 @@ var Server = class {
76
76
  };
77
77
  // ---- 商品查询订阅者 ----
78
78
  this.productQuerySubscribers = /* @__PURE__ */ new Map();
79
+ /** subscriberId → 智能定价变价时间点定时器句柄(与 subscriber 分离存储,便于 clear) */
80
+ this.productQueryScheduleTimers = /* @__PURE__ */ new Map();
79
81
  // ---- 订单 / 预约列表查询订阅者 ----
80
82
  this.orderQuerySubscribers = /* @__PURE__ */ new Map();
81
83
  this.bookingQuerySubscribers = /* @__PURE__ */ new Map();
@@ -208,7 +210,7 @@ var Server = class {
208
210
  */
209
211
  this.handleProductQuery = async ({ url, method, data, config }) => {
210
212
  console.log("[Server] handleProductQuery:", url, method, data, config);
211
- const { menu_list_ids, schedule_datetime, schedule_date, customer_id, ids, extension_type } = data;
213
+ const { menu_list_ids, schedule_datetime, schedule_date, customer_id, ids, extension_type, strategy_context } = data;
212
214
  const { callback, subscriberId } = config || {};
213
215
  this.logInfo("handleProductQuery: 开始处理商品查询请求", {
214
216
  menu_list_ids,
@@ -216,19 +218,32 @@ var Server = class {
216
218
  schedule_datetime,
217
219
  schedule_date,
218
220
  customer_id,
219
- extension_type
221
+ extension_type,
222
+ strategyContextKeys: strategy_context ? Object.keys(strategy_context) : []
220
223
  });
221
224
  if (subscriberId && typeof callback === "function") {
222
225
  this.productQuerySubscribers.set(subscriberId, {
223
226
  callback,
224
- context: { menu_list_ids, ids, schedule_date, schedule_datetime, customer_id, extension_type }
227
+ context: { menu_list_ids, ids, schedule_date, schedule_datetime, customer_id, extension_type, strategy_context }
225
228
  });
226
229
  this.logInfo("handleProductQuery: 已注册订阅者", {
227
230
  subscriberId,
228
231
  totalSubscribers: this.productQuerySubscribers.size
229
232
  });
230
233
  }
231
- return this.computeProductQueryResult({ menu_list_ids, ids, schedule_date, schedule_datetime, customer_id, extension_type });
234
+ const result = await this.computeProductQueryResult({
235
+ menu_list_ids,
236
+ ids,
237
+ schedule_date,
238
+ schedule_datetime,
239
+ customer_id,
240
+ extension_type,
241
+ strategy_context
242
+ });
243
+ if (subscriberId && typeof callback === "function") {
244
+ this.syncProductQueryScheduleTimers(subscriberId, strategy_context);
245
+ }
246
+ return result;
232
247
  };
233
248
  /**
234
249
  * 按商品 id 查询单条(GET /shop/product/query/:productId)
@@ -1911,6 +1926,7 @@ var Server = class {
1911
1926
  */
1912
1927
  removeProductQuerySubscriber(subscriberId) {
1913
1928
  if (subscriberId) {
1929
+ this.clearSubscriberScheduleTimers(subscriberId);
1914
1930
  this.productQuerySubscribers.delete(subscriberId);
1915
1931
  this.logInfo("removeProductQuerySubscriber: 已移除订阅者", {
1916
1932
  subscriberId,
@@ -1918,6 +1934,156 @@ var Server = class {
1918
1934
  });
1919
1935
  }
1920
1936
  }
1937
+ /**
1938
+ * 构建商品 query 响应 data 段,附带智能定价 schedule_time_points 元数据。
1939
+ */
1940
+ buildProductQueryResultPayload(list, count) {
1941
+ var _a, _b;
1942
+ return {
1943
+ list,
1944
+ count: count ?? list.length,
1945
+ schedule_time_points: ((_b = (_a = this.products) == null ? void 0 : _a.getLastScheduleTimePoints) == null ? void 0 : _b.call(_a)) ?? []
1946
+ };
1947
+ }
1948
+ /**
1949
+ * 是否启用商品 query 订阅的 schedule 时间点定时重算。
1950
+ * strategy_context.enable_schedule_reload === false 时关闭。
1951
+ */
1952
+ shouldScheduleProductQueryReload(strategy_context) {
1953
+ return (strategy_context == null ? void 0 : strategy_context.enable_schedule_reload) !== false;
1954
+ }
1955
+ /**
1956
+ * 清除指定 subscriber 的全部 schedule 定时器,防止重复 query 或 unsubscribe 后泄漏。
1957
+ */
1958
+ clearSubscriberScheduleTimers(subscriberId) {
1959
+ const timers = this.productQueryScheduleTimers.get(subscriberId);
1960
+ if (timers == null ? void 0 : timers.length) {
1961
+ timers.forEach((timerId) => clearTimeout(timerId));
1962
+ }
1963
+ this.productQueryScheduleTimers.delete(subscriberId);
1964
+ const subscriber = this.productQuerySubscribers.get(subscriberId);
1965
+ if (subscriber) {
1966
+ subscriber.scheduleTimerIds = [];
1967
+ }
1968
+ }
1969
+ /**
1970
+ * 从 productQueryScheduleTimers 移除已触发的 timer 引用。
1971
+ */
1972
+ removeScheduleTimerRef(subscriberId, timerId) {
1973
+ const timers = this.productQueryScheduleTimers.get(subscriberId);
1974
+ if (!(timers == null ? void 0 : timers.length))
1975
+ return;
1976
+ const next = timers.filter((id) => id !== timerId);
1977
+ if (next.length === 0) {
1978
+ this.productQueryScheduleTimers.delete(subscriberId);
1979
+ } else {
1980
+ this.productQueryScheduleTimers.set(subscriberId, next);
1981
+ }
1982
+ const subscriber = this.productQuerySubscribers.get(subscriberId);
1983
+ if (subscriber) {
1984
+ subscriber.scheduleTimerIds = next;
1985
+ }
1986
+ }
1987
+ /**
1988
+ * 计算 schedule 变价时间点到当前时刻的延迟(ms)。已过期返回 -1。
1989
+ */
1990
+ computeScheduleTimePointDelayMs(scheduleDate, timePoint) {
1991
+ const parts = timePoint.split(":");
1992
+ if (parts.length < 2)
1993
+ return -1;
1994
+ const hours = parseInt(parts[0], 10);
1995
+ const minutes = parseInt(parts[1], 10);
1996
+ if (!Number.isFinite(hours) || !Number.isFinite(minutes))
1997
+ return -1;
1998
+ const target = (0, import_dayjs.default)(scheduleDate).hour(hours).minute(minutes).second(4).millisecond(0);
1999
+ const delayMs = target.diff((0, import_dayjs.default)());
2000
+ return delayMs >= 0 ? delayMs : -1;
2001
+ }
2002
+ /**
2003
+ * 定时重算前刷新 subscriber schedule 上下文:使用执行瞬间时间,禁止沿用首次 query 的 schedule_datetime。
2004
+ */
2005
+ refreshSubscriberScheduleAtExecution(subscriber) {
2006
+ const executedAt = (0, import_dayjs.default)();
2007
+ const bookingDate = subscriber.context.schedule_date || executedAt.format("YYYY-MM-DD");
2008
+ subscriber.context.schedule_date = bookingDate;
2009
+ subscriber.context.schedule_datetime = `${bookingDate} ${executedAt.format("HH:mm:ss")}`;
2010
+ }
2011
+ /**
2012
+ * 为 subscriber 注册智能定价 scheduleTimePoints 定时重算。
2013
+ * 必须先 clearSubscriberScheduleTimers,再调用本方法。
2014
+ */
2015
+ scheduleSubscriberTimePointReloads(subscriberId, timePoints) {
2016
+ const subscriber = this.productQuerySubscribers.get(subscriberId);
2017
+ if (!subscriber || !timePoints.length)
2018
+ return;
2019
+ const scheduleDate = subscriber.context.schedule_date || (0, import_dayjs.default)().format("YYYY-MM-DD");
2020
+ const timerIds = [];
2021
+ for (const timePoint of timePoints) {
2022
+ const delayMs = this.computeScheduleTimePointDelayMs(scheduleDate, timePoint);
2023
+ if (delayMs < 0)
2024
+ continue;
2025
+ const timerId = setTimeout(() => {
2026
+ void this.onScheduleTimePointTimerFire(subscriberId, timerId);
2027
+ }, delayMs);
2028
+ timerIds.push(timerId);
2029
+ }
2030
+ if (timerIds.length > 0) {
2031
+ this.productQueryScheduleTimers.set(subscriberId, timerIds);
2032
+ subscriber.scheduleTimerIds = timerIds;
2033
+ this.logInfo("scheduleSubscriberTimePointReloads: 已注册定时器", {
2034
+ subscriberId,
2035
+ timerCount: timerIds.length,
2036
+ timePoints
2037
+ });
2038
+ }
2039
+ }
2040
+ /**
2041
+ * schedule 变价时间点定时器触发:刷新 schedule_datetime 后重算并 callback。
2042
+ */
2043
+ async onScheduleTimePointTimerFire(subscriberId, timerId) {
2044
+ this.removeScheduleTimerRef(subscriberId, timerId);
2045
+ if (!this.productQuerySubscribers.has(subscriberId))
2046
+ return;
2047
+ const subscriber = this.productQuerySubscribers.get(subscriberId);
2048
+ this.refreshSubscriberScheduleAtExecution(subscriber);
2049
+ await this.recomputeAndNotifySubscriber(subscriberId);
2050
+ }
2051
+ /**
2052
+ * 对单个商品 query 订阅者重算并推送(定时路径;不新建 schedule timer)。
2053
+ */
2054
+ async recomputeAndNotifySubscriber(subscriberId) {
2055
+ const subscriber = this.productQuerySubscribers.get(subscriberId);
2056
+ if (!subscriber)
2057
+ return;
2058
+ try {
2059
+ const result = await this.computeProductQueryResult(subscriber.context);
2060
+ subscriber.callback(result);
2061
+ this.logInfo("recomputeAndNotifySubscriber: 已推送", {
2062
+ subscriberId,
2063
+ schedule_datetime: subscriber.context.schedule_datetime
2064
+ });
2065
+ } catch (error) {
2066
+ const errorMessage = error instanceof Error ? error.message : String(error);
2067
+ this.logError("recomputeAndNotifySubscriber: 推送失败", {
2068
+ subscriberId,
2069
+ error: errorMessage
2070
+ });
2071
+ }
2072
+ }
2073
+ /**
2074
+ * query 完成后为 subscriber 同步 schedule 定时器(先 clear 再 schedule)。
2075
+ */
2076
+ syncProductQueryScheduleTimers(subscriberId, strategy_context) {
2077
+ var _a, _b;
2078
+ this.clearSubscriberScheduleTimers(subscriberId);
2079
+ if (!this.shouldScheduleProductQueryReload(strategy_context)) {
2080
+ return;
2081
+ }
2082
+ const timePoints = ((_b = (_a = this.products) == null ? void 0 : _a.getLastScheduleTimePoints) == null ? void 0 : _b.call(_a)) ?? [];
2083
+ if (timePoints.length > 0) {
2084
+ this.scheduleSubscriberTimePointReloads(subscriberId, timePoints);
2085
+ }
2086
+ }
1921
2087
  /**
1922
2088
  * 执行 sales 搜索并登记可见订单号。
1923
2089
  * 搜索参数完全透传后端,OS 不做过滤、筛选、排序。
@@ -4111,8 +4277,14 @@ var Server = class {
4111
4277
  */
4112
4278
  async computeProductQueryResult(context, options) {
4113
4279
  const tTotal = performance.now();
4114
- const { menu_list_ids, ids, schedule_date, schedule_datetime, customer_id, extension_type, product_id } = context;
4280
+ const { menu_list_ids, ids, schedule_date, schedule_datetime, customer_id, extension_type, product_id, strategy_context } = context;
4115
4281
  const queryIds = Array.isArray(ids) ? ids.map((id) => Number(id)).filter((id) => Number.isFinite(id)) : [];
4282
+ const formatterContext = this.buildProductFormatterContext({
4283
+ schedule_date,
4284
+ schedule_datetime,
4285
+ customer_id,
4286
+ strategy_context
4287
+ });
4116
4288
  this.logInfo("computeProductQueryResult 开始", {
4117
4289
  menuListIdsCount: (menu_list_ids == null ? void 0 : menu_list_ids.length) ?? 0,
4118
4290
  ids: queryIds,
@@ -4121,20 +4293,17 @@ var Server = class {
4121
4293
  customer_id,
4122
4294
  extension_type,
4123
4295
  product_id,
4296
+ strategyContextKeys: strategy_context ? Object.keys(strategy_context) : [],
4124
4297
  changedIds: options == null ? void 0 : options.changedIds
4125
4298
  });
4126
4299
  if (!this.products) {
4127
4300
  this.logError("computeProductQueryResult: Products 模块未注册");
4128
- return { message: "Products 模块未注册", data: { list: [], count: 0 } };
4301
+ return { message: "Products 模块未注册", data: this.buildProductQueryResultPayload([], 0) };
4129
4302
  }
4130
4303
  if (queryIds.length > 0) {
4131
4304
  const uniqueIds = Array.from(new Set(queryIds));
4132
4305
  const tPrice2 = performance.now();
4133
- const productsWithPrice = await this.products.getProductsWithPrice(schedule_date, {
4134
- scheduleModule: this.getSchedule(),
4135
- schedule_datetime,
4136
- customer_id
4137
- }, {
4306
+ const productsWithPrice = await this.products.getProductsWithPrice(schedule_date, formatterContext, {
4138
4307
  changedIds: options == null ? void 0 : options.changedIds,
4139
4308
  productIds: uniqueIds
4140
4309
  });
@@ -4155,7 +4324,7 @@ var Server = class {
4155
4324
  });
4156
4325
  return {
4157
4326
  code: 200,
4158
- data: { list: extensionFilteredProducts, count: extensionFilteredProducts.length },
4327
+ data: this.buildProductQueryResultPayload(extensionFilteredProducts),
4159
4328
  message: "",
4160
4329
  status: true
4161
4330
  };
@@ -4163,9 +4332,7 @@ var Server = class {
4163
4332
  if (product_id != null && Number.isFinite(Number(product_id))) {
4164
4333
  const pid = Number(product_id);
4165
4334
  const tPrice2 = performance.now();
4166
- const allProductsWithPrice = await this.products.getProductsWithPrice(schedule_date, {
4167
- scheduleModule: this.getSchedule()
4168
- }, {
4335
+ const allProductsWithPrice = await this.products.getProductsWithPrice(schedule_date, formatterContext, {
4169
4336
  changedIds: options == null ? void 0 : options.changedIds
4170
4337
  });
4171
4338
  (0, import_product.perfMark)("computeQuery.getProductsWithPrice(single)", performance.now() - tPrice2, {
@@ -4195,11 +4362,7 @@ var Server = class {
4195
4362
  }
4196
4363
  if (this.shouldQueryProductsByExtensionTypes(extension_type)) {
4197
4364
  const tPrice2 = performance.now();
4198
- let filteredProducts2 = await this.products.getProductsWithPrice(schedule_date, {
4199
- scheduleModule: this.getSchedule(),
4200
- schedule_datetime,
4201
- customer_id
4202
- }, {
4365
+ let filteredProducts2 = await this.products.getProductsWithPrice(schedule_date, formatterContext, {
4203
4366
  changedIds: options == null ? void 0 : options.changedIds
4204
4367
  });
4205
4368
  (0, import_product.perfMark)("computeQuery.getProductsWithPrice(extensionType)", performance.now() - tPrice2, {
@@ -4239,18 +4402,18 @@ var Server = class {
4239
4402
  });
4240
4403
  return {
4241
4404
  code: 200,
4242
- data: { list: filteredProducts2, count: filteredProducts2.length },
4405
+ data: this.buildProductQueryResultPayload(filteredProducts2),
4243
4406
  message: "",
4244
4407
  status: true
4245
4408
  };
4246
4409
  }
4247
4410
  if (!this.menu) {
4248
4411
  this.logError("computeProductQueryResult: Menu 模块未注册");
4249
- return { message: "Menu 模块未注册", data: { list: [], count: 0 } };
4412
+ return { message: "Menu 模块未注册", data: this.buildProductQueryResultPayload([], 0) };
4250
4413
  }
4251
4414
  if (!this.schedule) {
4252
4415
  this.logError("computeProductQueryResult: Schedule 模块未注册");
4253
- return { message: "Schedule 模块未注册", data: { list: [], count: 0 } };
4416
+ return { message: "Schedule 模块未注册", data: this.buildProductQueryResultPayload([], 0) };
4254
4417
  }
4255
4418
  let activeMenuList = [];
4256
4419
  if (menu_list_ids && Array.isArray(menu_list_ids) && menu_list_ids.length > 0) {
@@ -4274,11 +4437,7 @@ var Server = class {
4274
4437
  isAllProducts: !!productScope.isAllProducts
4275
4438
  });
4276
4439
  const tPrice = performance.now();
4277
- let filteredProducts = scopedProductIds.length > 0 ? await this.products.getProductsWithPrice(schedule_date, {
4278
- scheduleModule: this.getSchedule(),
4279
- schedule_datetime,
4280
- customer_id
4281
- }, {
4440
+ let filteredProducts = scopedProductIds.length > 0 ? await this.products.getProductsWithPrice(schedule_date, formatterContext, {
4282
4441
  changedIds: options == null ? void 0 : options.changedIds,
4283
4442
  productIds: productScope.isAllProducts ? void 0 : scopedProductIds
4284
4443
  }) : [];
@@ -4321,11 +4480,29 @@ var Server = class {
4321
4480
  });
4322
4481
  return {
4323
4482
  code: 200,
4324
- data: { list: filteredProducts, count: filteredProducts.length },
4483
+ data: this.buildProductQueryResultPayload(filteredProducts),
4325
4484
  message: "",
4326
4485
  status: true
4327
4486
  };
4328
4487
  }
4488
+ /**
4489
+ * 构建商品格式化上下文。
4490
+ *
4491
+ * 智能定价条件使用全量 menuList/scheduleList;Product Query 顶层参数只保留筛选商品集合所需字段。
4492
+ */
4493
+ buildProductFormatterContext(context) {
4494
+ var _a, _b, _c, _d;
4495
+ const scheduleModule = this.getSchedule();
4496
+ return {
4497
+ scheduleModule,
4498
+ schedule_datetime: context.schedule_datetime,
4499
+ customer_id: context.customer_id,
4500
+ strategy_context: context.strategy_context,
4501
+ menuList: ((_b = (_a = this.menu) == null ? void 0 : _a.getMenuList) == null ? void 0 : _b.call(_a)) || [],
4502
+ scheduleList: ((_c = scheduleModule == null ? void 0 : scheduleModule.getScheduleList) == null ? void 0 : _c.call(scheduleModule)) || [],
4503
+ dataVariantEvaluator: (_d = this.core.context) == null ? void 0 : _d.dataVariantEvaluator
4504
+ };
4505
+ }
4329
4506
  /**
4330
4507
  * 数据变更后,遍历所有订阅者重新计算查询结果并通过 callback 推送
4331
4508
  * 由 ProductsModule 的 onProductsSyncCompleted 事件触发
@@ -43,6 +43,16 @@ export declare class OrderModule extends BaseModule implements Module {
43
43
  */
44
44
  private logError;
45
45
  private logWarning;
46
+ /**
47
+ * 在订单进入内存或 SQLite 前统一压缩子项强身份重复并补齐协议 UID。
48
+ * 仅记录计数与订单标识,不输出商品、预约、支付或客户明细。
49
+ */
50
+ private normalizeOrderCollectionsForIngress;
51
+ /**
52
+ * incoming 决定权威成员集合;同一持久化行仍从 existing 补齐 incoming 省略的本地展示字段。
53
+ */
54
+ private mergeAuthoritativeOrderCollection;
55
+ private reconcileOverwriteOrderCollections;
46
56
  /**
47
57
  * 记录订单最近一次 SQLite 写入,供 pubsub 回声去重使用。
48
58
  *
@@ -212,10 +222,6 @@ export declare class OrderModule extends BaseModule implements Module {
212
222
  private findOrderIndexByIdentity;
213
223
  private getOrderCompletenessScore;
214
224
  private pickPreferredOrder;
215
- private getProductIdentityKeys;
216
- private mergeOrderProductLists;
217
- private getPaymentMergeKey;
218
- private mergeOrderPaymentLists;
219
225
  private mergeOrderRecords;
220
226
  private summarizeDuplicateOrders;
221
227
  private logDuplicateOrders;