@pisell/pisellos 2.2.261 → 2.2.263

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 (134) 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/model/strategy/adapter/promotion/adapter.d.ts +0 -4
  14. package/dist/model/strategy/adapter/promotion/adapter.js +0 -23
  15. package/dist/model/strategy/adapter/promotion/evaluator.d.ts +0 -17
  16. package/dist/model/strategy/adapter/promotion/evaluator.js +6 -279
  17. package/dist/model/strategy/adapter/promotion/examples.d.ts +0 -86
  18. package/dist/model/strategy/adapter/promotion/examples.js +0 -99
  19. package/dist/model/strategy/adapter/promotion/index.d.ts +1 -1
  20. package/dist/model/strategy/adapter/promotion/index.js +1 -1
  21. package/dist/model/strategy/adapter/promotion/type.d.ts +2 -90
  22. package/dist/model/strategy/adapter/promotion/type.js +0 -45
  23. package/dist/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +16 -10
  24. package/dist/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.js +2 -1
  25. package/dist/modules/BookingContext/utils/cacheItemToBookingInput.d.ts +1 -1
  26. package/dist/modules/BookingContext/utils/cacheItemToBookingInput.js +32 -1
  27. package/dist/modules/Discount/index.d.ts +5 -2
  28. package/dist/modules/Discount/index.js +30 -7
  29. package/dist/modules/Discount/types.d.ts +4 -0
  30. package/dist/modules/Order/index.d.ts +14 -2
  31. package/dist/modules/Order/index.js +1035 -736
  32. package/dist/modules/Order/types.d.ts +16 -0
  33. package/dist/modules/Order/utils/orderCollectionIdentity.d.ts +53 -0
  34. package/dist/modules/Order/utils/orderCollectionIdentity.js +405 -0
  35. package/dist/modules/Order/utils.d.ts +4 -3
  36. package/dist/modules/Order/utils.js +54 -61
  37. package/dist/modules/Product/types.d.ts +22 -0
  38. package/dist/modules/ProductList/index.d.ts +1 -1
  39. package/dist/modules/ProductList/index.js +4 -2
  40. package/dist/modules/ProductList/types.d.ts +2 -0
  41. package/dist/modules/Rules/index.d.ts +2 -9
  42. package/dist/modules/Rules/index.js +69 -43
  43. package/dist/modules/Rules/types.d.ts +10 -1
  44. package/dist/server/index.d.ts +55 -0
  45. package/dist/server/index.js +1173 -753
  46. package/dist/server/modules/order/index.d.ts +10 -4
  47. package/dist/server/modules/order/index.js +404 -399
  48. package/dist/server/modules/order/types.d.ts +4 -0
  49. package/dist/server/modules/products/index.d.ts +31 -8
  50. package/dist/server/modules/products/index.js +549 -390
  51. package/dist/server/modules/products/types.d.ts +18 -0
  52. package/dist/solution/BaseSales/index.d.ts +21 -1
  53. package/dist/solution/BaseSales/index.js +1136 -789
  54. package/dist/solution/BaseSales/types.d.ts +6 -1
  55. package/dist/solution/BaseSales/types.js +1 -1
  56. package/dist/solution/BaseSales/utils/cartPromotion.d.ts +1 -3
  57. package/dist/solution/BaseSales/utils/cartPromotion.js +48 -81
  58. package/dist/solution/BaseSales/utils/parseSalesResponse.d.ts +3 -3
  59. package/dist/solution/BaseSales/utils/parseSalesResponse.js +6 -8
  60. package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +14 -1
  61. package/dist/solution/BookingByStep/index.d.ts +1 -1
  62. package/dist/solution/BookingTicket/index.d.ts +1 -1
  63. package/dist/solution/BookingTicket/index.js +59 -25
  64. package/dist/solution/BookingTicket/types.d.ts +2 -0
  65. package/dist/solution/ScanOrder/index.js +20 -31
  66. package/dist/solution/ShopDiscount/index.js +15 -14
  67. package/dist/solution/VenueBooking/index.js +19 -30
  68. package/lib/model/strategy/adapter/dataVariant/adapter.d.ts +50 -0
  69. package/lib/model/strategy/adapter/dataVariant/adapter.js +149 -0
  70. package/lib/model/strategy/adapter/dataVariant/evaluator.d.ts +89 -0
  71. package/lib/model/strategy/adapter/dataVariant/evaluator.js +583 -0
  72. package/lib/model/strategy/adapter/dataVariant/examples.d.ts +39 -0
  73. package/lib/model/strategy/adapter/dataVariant/examples.js +293 -0
  74. package/lib/model/strategy/adapter/dataVariant/index.d.ts +4 -0
  75. package/lib/model/strategy/adapter/dataVariant/index.js +38 -0
  76. package/lib/model/strategy/adapter/dataVariant/type.d.ts +148 -0
  77. package/lib/model/strategy/adapter/dataVariant/type.js +31 -0
  78. package/lib/model/strategy/adapter/index.d.ts +4 -0
  79. package/lib/model/strategy/adapter/index.js +13 -2
  80. package/lib/model/strategy/adapter/promotion/adapter.d.ts +0 -4
  81. package/lib/model/strategy/adapter/promotion/adapter.js +0 -20
  82. package/lib/model/strategy/adapter/promotion/evaluator.d.ts +0 -17
  83. package/lib/model/strategy/adapter/promotion/evaluator.js +0 -235
  84. package/lib/model/strategy/adapter/promotion/examples.d.ts +0 -86
  85. package/lib/model/strategy/adapter/promotion/examples.js +0 -91
  86. package/lib/model/strategy/adapter/promotion/index.d.ts +1 -1
  87. package/lib/model/strategy/adapter/promotion/index.js +0 -51
  88. package/lib/model/strategy/adapter/promotion/type.d.ts +2 -90
  89. package/lib/model/strategy/adapter/promotion/type.js +0 -2
  90. package/lib/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +5 -0
  91. package/lib/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.js +1 -0
  92. package/lib/modules/BookingContext/utils/cacheItemToBookingInput.d.ts +1 -1
  93. package/lib/modules/BookingContext/utils/cacheItemToBookingInput.js +23 -2
  94. package/lib/modules/Discount/index.d.ts +5 -2
  95. package/lib/modules/Discount/index.js +23 -3
  96. package/lib/modules/Discount/types.d.ts +4 -0
  97. package/lib/modules/Order/index.d.ts +14 -2
  98. package/lib/modules/Order/index.js +326 -60
  99. package/lib/modules/Order/types.d.ts +16 -0
  100. package/lib/modules/Order/utils/orderCollectionIdentity.d.ts +53 -0
  101. package/lib/modules/Order/utils/orderCollectionIdentity.js +415 -0
  102. package/lib/modules/Order/utils.d.ts +4 -3
  103. package/lib/modules/Order/utils.js +22 -23
  104. package/lib/modules/Product/types.d.ts +22 -0
  105. package/lib/modules/ProductList/index.d.ts +1 -1
  106. package/lib/modules/ProductList/index.js +4 -2
  107. package/lib/modules/ProductList/types.d.ts +2 -0
  108. package/lib/modules/Rules/index.d.ts +2 -9
  109. package/lib/modules/Rules/index.js +61 -29
  110. package/lib/modules/Rules/types.d.ts +10 -1
  111. package/lib/server/index.d.ts +55 -0
  112. package/lib/server/index.js +332 -34
  113. package/lib/server/modules/order/index.d.ts +10 -4
  114. package/lib/server/modules/order/index.js +198 -139
  115. package/lib/server/modules/order/types.d.ts +4 -0
  116. package/lib/server/modules/products/index.d.ts +31 -8
  117. package/lib/server/modules/products/index.js +134 -35
  118. package/lib/server/modules/products/types.d.ts +18 -0
  119. package/lib/solution/BaseSales/index.d.ts +21 -1
  120. package/lib/solution/BaseSales/index.js +313 -72
  121. package/lib/solution/BaseSales/types.d.ts +6 -1
  122. package/lib/solution/BaseSales/utils/cartPromotion.d.ts +1 -3
  123. package/lib/solution/BaseSales/utils/cartPromotion.js +29 -60
  124. package/lib/solution/BaseSales/utils/parseSalesResponse.d.ts +3 -3
  125. package/lib/solution/BaseSales/utils/parseSalesResponse.js +4 -5
  126. package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +8 -0
  127. package/lib/solution/BookingByStep/index.d.ts +1 -1
  128. package/lib/solution/BookingTicket/index.d.ts +1 -1
  129. package/lib/solution/BookingTicket/index.js +33 -8
  130. package/lib/solution/BookingTicket/types.d.ts +2 -0
  131. package/lib/solution/ScanOrder/index.js +0 -8
  132. package/lib/solution/ShopDiscount/index.js +2 -1
  133. package/lib/solution/VenueBooking/index.js +0 -8
  134. package/package.json +1 -1
@@ -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)
@@ -769,12 +784,13 @@ var Server = class {
769
784
  if (!this.shouldForceRemoteSalesDetail(data)) {
770
785
  const localOrder = await this.order.getLocalOrderByLookup(lookup);
771
786
  if (localOrder) {
787
+ const enrichedOrder = await this.withSalesDetailProductSnapshots(localOrder);
772
788
  this.logInfo("handleOrderSalesDetail: 命中本地订单", { lookup });
773
789
  return {
774
790
  code: 200,
775
791
  status: true,
776
792
  message: "",
777
- data: localOrder
793
+ data: enrichedOrder
778
794
  };
779
795
  }
780
796
  }
@@ -821,11 +837,12 @@ var Server = class {
821
837
  };
822
838
  }
823
839
  this.logInfo("handleOrderSalesDetail: 远端订单已同步到本地", { lookup });
840
+ const enrichedOrder = await this.withSalesDetailProductSnapshots(savedOrder);
824
841
  return {
825
842
  code: 200,
826
843
  status: true,
827
844
  message: "",
828
- data: savedOrder
845
+ data: enrichedOrder
829
846
  };
830
847
  } catch (error) {
831
848
  const errorMessage = error instanceof Error ? error.message : String(error);
@@ -1909,6 +1926,7 @@ var Server = class {
1909
1926
  */
1910
1927
  removeProductQuerySubscriber(subscriberId) {
1911
1928
  if (subscriberId) {
1929
+ this.clearSubscriberScheduleTimers(subscriberId);
1912
1930
  this.productQuerySubscribers.delete(subscriberId);
1913
1931
  this.logInfo("removeProductQuerySubscriber: 已移除订阅者", {
1914
1932
  subscriberId,
@@ -1916,6 +1934,156 @@ var Server = class {
1916
1934
  });
1917
1935
  }
1918
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
+ }
1919
2087
  /**
1920
2088
  * 执行 sales 搜索并登记可见订单号。
1921
2089
  * 搜索参数完全透传后端,OS 不做过滤、筛选、排序。
@@ -2938,6 +3106,7 @@ var Server = class {
2938
3106
  });
2939
3107
  }
2940
3108
  async handleOrderCheckoutSubmit(params) {
3109
+ var _a;
2941
3110
  const { backendPath, data, title } = params;
2942
3111
  this.logInfo(`${title}: 开始处理`, {
2943
3112
  backendPath,
@@ -2962,9 +3131,10 @@ var Server = class {
2962
3131
  });
2963
3132
  if ((pendingResult == null ? void 0 : pendingResult.status) === true) {
2964
3133
  const pendingOrder = pendingResult.data;
2965
- if (pendingOrder && this.shouldBuildSmallTicketData(checkoutData) && this.isSalesOrderFullyPaid(pendingOrder) && // TODO 这里先加个兼容,只有 type virtual 才走打印
2966
- // 后期需要迁移到 picoding ,根据工作流配置
2967
- checkoutData.type === "virtual") {
3134
+ const platform = (_a = this.core.context) == null ? void 0 : _a.getPlatform();
3135
+ const isAndroid = platform === "android";
3136
+ if (pendingOrder && this.shouldBuildSmallTicketData(checkoutData) && this.isSalesOrderFullyPaid(pendingOrder) && // TODO 这里先加个兼容,如果是安卓,则需要 type=virtual 才走打印,如果是苹果则无限制
3137
+ (isAndroid ? checkoutData.type === "virtual" : true)) {
2968
3138
  await this.dispatchLocalReceiptPrint({
2969
3139
  checkoutData,
2970
3140
  order: pendingOrder,
@@ -3231,6 +3401,123 @@ var Server = class {
3231
3401
  return map;
3232
3402
  }, {});
3233
3403
  }
3404
+ collectSalesDetailProductIds(order) {
3405
+ const products = Array.isArray(order == null ? void 0 : order.products) ? order.products : [];
3406
+ const productIds = /* @__PURE__ */ new Set();
3407
+ const addProductId = (rawProductId) => {
3408
+ if (rawProductId === void 0 || rawProductId === null || rawProductId === "")
3409
+ return;
3410
+ const productId = Number(rawProductId);
3411
+ if (Number.isFinite(productId))
3412
+ productIds.add(productId);
3413
+ };
3414
+ products.forEach((product) => {
3415
+ addProductId((product == null ? void 0 : product.product_id) ?? (product == null ? void 0 : product.id));
3416
+ if (Array.isArray(product == null ? void 0 : product.product_bundle)) {
3417
+ product.product_bundle.forEach((bundle) => {
3418
+ addProductId((bundle == null ? void 0 : bundle.bundle_product_id) ?? (bundle == null ? void 0 : bundle._bundle_product_id) ?? (bundle == null ? void 0 : bundle.product_id));
3419
+ });
3420
+ }
3421
+ });
3422
+ return Array.from(productIds);
3423
+ }
3424
+ async buildSalesDetailProductSnapshotMap(order) {
3425
+ var _a;
3426
+ const productIds = this.collectSalesDetailProductIds(order);
3427
+ const getSnapshots = (_a = this.products) == null ? void 0 : _a.getProductSnapshotsByIds;
3428
+ if (!productIds.length || typeof getSnapshots !== "function")
3429
+ return {};
3430
+ try {
3431
+ return await getSnapshots.call(this.products, productIds);
3432
+ } catch (error) {
3433
+ this.logWarning("buildSalesDetailProductSnapshotMap: 查询本地商品快照失败", {
3434
+ product_ids: productIds,
3435
+ error: error instanceof Error ? error.message : String(error)
3436
+ });
3437
+ return {};
3438
+ }
3439
+ }
3440
+ withProductCatalogSnapshots(product, snapshotMap) {
3441
+ if (!product || typeof product !== "object")
3442
+ return product;
3443
+ const rawProductId = (product == null ? void 0 : product.product_id) ?? (product == null ? void 0 : product.id);
3444
+ if (rawProductId === void 0 || rawProductId === null || rawProductId === "")
3445
+ return product;
3446
+ const productId = Number(rawProductId);
3447
+ if (!Number.isFinite(productId))
3448
+ return product;
3449
+ const snapshot = snapshotMap[String(productId)];
3450
+ if (!snapshot)
3451
+ return product;
3452
+ const shouldFillCover = (product.product_cover === void 0 || product.product_cover === null || product.product_cover === "") && snapshot.cover !== void 0 && snapshot.cover !== null && snapshot.cover !== "";
3453
+ const metadata = product.metadata && typeof product.metadata === "object" ? product.metadata : {};
3454
+ const shouldFillHolderConfig = (metadata.holder_config === void 0 || metadata.holder_config === null) && snapshot.holder_config !== void 0 && snapshot.holder_config !== null;
3455
+ if (!shouldFillCover && !shouldFillHolderConfig)
3456
+ return product;
3457
+ return {
3458
+ ...product,
3459
+ ...shouldFillCover ? { product_cover: snapshot.cover } : {},
3460
+ ...shouldFillHolderConfig ? {
3461
+ metadata: {
3462
+ ...metadata,
3463
+ holder_config: snapshot.holder_config
3464
+ }
3465
+ } : {}
3466
+ };
3467
+ }
3468
+ withBundleCatalogSnapshots(product, snapshotMap) {
3469
+ if (!Array.isArray(product == null ? void 0 : product.product_bundle))
3470
+ return product;
3471
+ let hasChanged = false;
3472
+ const productBundle = product.product_bundle.map((bundle) => {
3473
+ if (!bundle || typeof bundle !== "object")
3474
+ return bundle;
3475
+ const rawProductId = (bundle == null ? void 0 : bundle.bundle_product_id) ?? (bundle == null ? void 0 : bundle._bundle_product_id) ?? (bundle == null ? void 0 : bundle.product_id);
3476
+ if (rawProductId === void 0 || rawProductId === null || rawProductId === "")
3477
+ return bundle;
3478
+ const productId = Number(rawProductId);
3479
+ if (!Number.isFinite(productId))
3480
+ return bundle;
3481
+ const snapshot = snapshotMap[String(productId)];
3482
+ const shouldFillCover = (bundle.cover === void 0 || bundle.cover === null || bundle.cover === "") && (snapshot == null ? void 0 : snapshot.cover) !== void 0 && snapshot.cover !== null && snapshot.cover !== "";
3483
+ if (!shouldFillCover)
3484
+ return bundle;
3485
+ hasChanged = true;
3486
+ return {
3487
+ ...bundle,
3488
+ cover: snapshot.cover
3489
+ };
3490
+ });
3491
+ if (!hasChanged)
3492
+ return product;
3493
+ return {
3494
+ ...product,
3495
+ product_bundle: productBundle
3496
+ };
3497
+ }
3498
+ async withSalesDetailProductSnapshots(order) {
3499
+ const products = Array.isArray(order == null ? void 0 : order.products) ? order.products : null;
3500
+ if (!(products == null ? void 0 : products.length))
3501
+ return order;
3502
+ const snapshotMap = await this.buildSalesDetailProductSnapshotMap(order);
3503
+ if (!Object.keys(snapshotMap).length)
3504
+ return order;
3505
+ let hasChanged = false;
3506
+ const enrichedProducts = products.map((product) => {
3507
+ const productWithBundleSnapshots = this.withBundleCatalogSnapshots(product, snapshotMap);
3508
+ const nextProduct = this.withProductCatalogSnapshots(productWithBundleSnapshots, snapshotMap);
3509
+ if (nextProduct !== product)
3510
+ hasChanged = true;
3511
+ return nextProduct;
3512
+ });
3513
+ if (!hasChanged)
3514
+ return order;
3515
+ const enrichedOrder = {
3516
+ ...order,
3517
+ products: enrichedProducts
3518
+ };
3519
+ return enrichedOrder;
3520
+ }
3234
3521
  withPendingSyncProductTitles(order, productMap) {
3235
3522
  const products = Array.isArray(order.products) ? order.products : null;
3236
3523
  if (!(products == null ? void 0 : products.length))
@@ -3992,8 +4279,14 @@ var Server = class {
3992
4279
  */
3993
4280
  async computeProductQueryResult(context, options) {
3994
4281
  const tTotal = performance.now();
3995
- const { menu_list_ids, ids, schedule_date, schedule_datetime, customer_id, extension_type, product_id } = context;
4282
+ const { menu_list_ids, ids, schedule_date, schedule_datetime, customer_id, extension_type, product_id, strategy_context } = context;
3996
4283
  const queryIds = Array.isArray(ids) ? ids.map((id) => Number(id)).filter((id) => Number.isFinite(id)) : [];
4284
+ const formatterContext = this.buildProductFormatterContext({
4285
+ schedule_date,
4286
+ schedule_datetime,
4287
+ customer_id,
4288
+ strategy_context
4289
+ });
3997
4290
  this.logInfo("computeProductQueryResult 开始", {
3998
4291
  menuListIdsCount: (menu_list_ids == null ? void 0 : menu_list_ids.length) ?? 0,
3999
4292
  ids: queryIds,
@@ -4002,20 +4295,17 @@ var Server = class {
4002
4295
  customer_id,
4003
4296
  extension_type,
4004
4297
  product_id,
4298
+ strategyContextKeys: strategy_context ? Object.keys(strategy_context) : [],
4005
4299
  changedIds: options == null ? void 0 : options.changedIds
4006
4300
  });
4007
4301
  if (!this.products) {
4008
4302
  this.logError("computeProductQueryResult: Products 模块未注册");
4009
- return { message: "Products 模块未注册", data: { list: [], count: 0 } };
4303
+ return { message: "Products 模块未注册", data: this.buildProductQueryResultPayload([], 0) };
4010
4304
  }
4011
4305
  if (queryIds.length > 0) {
4012
4306
  const uniqueIds = Array.from(new Set(queryIds));
4013
4307
  const tPrice2 = performance.now();
4014
- const productsWithPrice = await this.products.getProductsWithPrice(schedule_date, {
4015
- scheduleModule: this.getSchedule(),
4016
- schedule_datetime,
4017
- customer_id
4018
- }, {
4308
+ const productsWithPrice = await this.products.getProductsWithPrice(schedule_date, formatterContext, {
4019
4309
  changedIds: options == null ? void 0 : options.changedIds,
4020
4310
  productIds: uniqueIds
4021
4311
  });
@@ -4036,7 +4326,7 @@ var Server = class {
4036
4326
  });
4037
4327
  return {
4038
4328
  code: 200,
4039
- data: { list: extensionFilteredProducts, count: extensionFilteredProducts.length },
4329
+ data: this.buildProductQueryResultPayload(extensionFilteredProducts),
4040
4330
  message: "",
4041
4331
  status: true
4042
4332
  };
@@ -4044,9 +4334,7 @@ var Server = class {
4044
4334
  if (product_id != null && Number.isFinite(Number(product_id))) {
4045
4335
  const pid = Number(product_id);
4046
4336
  const tPrice2 = performance.now();
4047
- const allProductsWithPrice = await this.products.getProductsWithPrice(schedule_date, {
4048
- scheduleModule: this.getSchedule()
4049
- }, {
4337
+ const allProductsWithPrice = await this.products.getProductsWithPrice(schedule_date, formatterContext, {
4050
4338
  changedIds: options == null ? void 0 : options.changedIds
4051
4339
  });
4052
4340
  (0, import_product.perfMark)("computeQuery.getProductsWithPrice(single)", performance.now() - tPrice2, {
@@ -4076,11 +4364,7 @@ var Server = class {
4076
4364
  }
4077
4365
  if (this.shouldQueryProductsByExtensionTypes(extension_type)) {
4078
4366
  const tPrice2 = performance.now();
4079
- let filteredProducts2 = await this.products.getProductsWithPrice(schedule_date, {
4080
- scheduleModule: this.getSchedule(),
4081
- schedule_datetime,
4082
- customer_id
4083
- }, {
4367
+ let filteredProducts2 = await this.products.getProductsWithPrice(schedule_date, formatterContext, {
4084
4368
  changedIds: options == null ? void 0 : options.changedIds
4085
4369
  });
4086
4370
  (0, import_product.perfMark)("computeQuery.getProductsWithPrice(extensionType)", performance.now() - tPrice2, {
@@ -4120,18 +4404,18 @@ var Server = class {
4120
4404
  });
4121
4405
  return {
4122
4406
  code: 200,
4123
- data: { list: filteredProducts2, count: filteredProducts2.length },
4407
+ data: this.buildProductQueryResultPayload(filteredProducts2),
4124
4408
  message: "",
4125
4409
  status: true
4126
4410
  };
4127
4411
  }
4128
4412
  if (!this.menu) {
4129
4413
  this.logError("computeProductQueryResult: Menu 模块未注册");
4130
- return { message: "Menu 模块未注册", data: { list: [], count: 0 } };
4414
+ return { message: "Menu 模块未注册", data: this.buildProductQueryResultPayload([], 0) };
4131
4415
  }
4132
4416
  if (!this.schedule) {
4133
4417
  this.logError("computeProductQueryResult: Schedule 模块未注册");
4134
- return { message: "Schedule 模块未注册", data: { list: [], count: 0 } };
4418
+ return { message: "Schedule 模块未注册", data: this.buildProductQueryResultPayload([], 0) };
4135
4419
  }
4136
4420
  let activeMenuList = [];
4137
4421
  if (menu_list_ids && Array.isArray(menu_list_ids) && menu_list_ids.length > 0) {
@@ -4155,11 +4439,7 @@ var Server = class {
4155
4439
  isAllProducts: !!productScope.isAllProducts
4156
4440
  });
4157
4441
  const tPrice = performance.now();
4158
- let filteredProducts = scopedProductIds.length > 0 ? await this.products.getProductsWithPrice(schedule_date, {
4159
- scheduleModule: this.getSchedule(),
4160
- schedule_datetime,
4161
- customer_id
4162
- }, {
4442
+ let filteredProducts = scopedProductIds.length > 0 ? await this.products.getProductsWithPrice(schedule_date, formatterContext, {
4163
4443
  changedIds: options == null ? void 0 : options.changedIds,
4164
4444
  productIds: productScope.isAllProducts ? void 0 : scopedProductIds
4165
4445
  }) : [];
@@ -4202,11 +4482,29 @@ var Server = class {
4202
4482
  });
4203
4483
  return {
4204
4484
  code: 200,
4205
- data: { list: filteredProducts, count: filteredProducts.length },
4485
+ data: this.buildProductQueryResultPayload(filteredProducts),
4206
4486
  message: "",
4207
4487
  status: true
4208
4488
  };
4209
4489
  }
4490
+ /**
4491
+ * 构建商品格式化上下文。
4492
+ *
4493
+ * 智能定价条件使用全量 menuList/scheduleList;Product Query 顶层参数只保留筛选商品集合所需字段。
4494
+ */
4495
+ buildProductFormatterContext(context) {
4496
+ var _a, _b, _c, _d;
4497
+ const scheduleModule = this.getSchedule();
4498
+ return {
4499
+ scheduleModule,
4500
+ schedule_datetime: context.schedule_datetime,
4501
+ customer_id: context.customer_id,
4502
+ strategy_context: context.strategy_context,
4503
+ menuList: ((_b = (_a = this.menu) == null ? void 0 : _a.getMenuList) == null ? void 0 : _b.call(_a)) || [],
4504
+ scheduleList: ((_c = scheduleModule == null ? void 0 : scheduleModule.getScheduleList) == null ? void 0 : _c.call(scheduleModule)) || [],
4505
+ dataVariantEvaluator: (_d = this.core.context) == null ? void 0 : _d.dataVariantEvaluator
4506
+ };
4507
+ }
4210
4508
  /**
4211
4509
  * 数据变更后,遍历所有订阅者重新计算查询结果并通过 callback 推送
4212
4510
  * 由 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;