@pisell/pisellos 2.2.291 → 2.2.293

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 (174) hide show
  1. package/dist/core/index.d.ts +7 -0
  2. package/dist/core/index.js +108 -65
  3. package/dist/model/strategy/adapter/itemRule/adapter.d.ts +4 -1
  4. package/dist/model/strategy/adapter/itemRule/adapter.js +135 -35
  5. package/dist/model/strategy/adapter/itemRule/evaluator.d.ts +8 -1
  6. package/dist/model/strategy/adapter/itemRule/evaluator.js +25 -1
  7. package/dist/model/strategy/adapter/itemRule/type.d.ts +44 -0
  8. package/dist/model/strategy/adapter/itemRule/type.js +19 -1
  9. package/dist/model/strategy/adapter/promotion/index.js +0 -9
  10. package/dist/modules/BookingContext/index.d.ts +3 -0
  11. package/dist/modules/BookingContext/index.js +42 -13
  12. package/dist/modules/BookingContext/types.d.ts +4 -4
  13. package/dist/modules/BookingContext/types.js +3 -3
  14. package/dist/modules/OpenData/types.d.ts +1 -0
  15. package/dist/modules/Order/index.d.ts +18 -3
  16. package/dist/modules/Order/index.js +885 -485
  17. package/dist/modules/Order/salesNotes.d.ts +31 -0
  18. package/dist/modules/Order/salesNotes.js +492 -0
  19. package/dist/modules/Order/types.d.ts +93 -5
  20. package/dist/modules/Order/types.js +5 -0
  21. package/dist/modules/Order/utils.d.ts +12 -0
  22. package/dist/modules/Order/utils.js +96 -31
  23. package/dist/modules/Product/types.d.ts +2 -0
  24. package/dist/modules/ProductList/clientDataVariants.d.ts +50 -0
  25. package/dist/modules/ProductList/clientDataVariants.js +216 -0
  26. package/dist/modules/ProductList/index.d.ts +18 -3
  27. package/dist/modules/ProductList/index.js +220 -38
  28. package/dist/modules/ProductList/types.d.ts +10 -0
  29. package/dist/modules/Quotation/index.d.ts +22 -0
  30. package/dist/modules/Quotation/index.js +293 -28
  31. package/dist/modules/ResourcePlanner/index.d.ts +0 -22
  32. package/dist/modules/ResourcePlanner/index.js +1 -180
  33. package/dist/modules/ResourcePlanner/localClock.d.ts +33 -0
  34. package/dist/modules/ResourcePlanner/localClock.js +183 -0
  35. package/dist/modules/ResourcePlanner/planner.d.ts +5 -14
  36. package/dist/modules/ResourcePlanner/planner.js +2111 -958
  37. package/dist/modules/ResourcePlanner/types.d.ts +359 -187
  38. package/dist/modules/ResourcePlanner/types.js +33 -1
  39. package/dist/plugins/request.d.ts +1 -0
  40. package/dist/server/index.d.ts +17 -2
  41. package/dist/server/index.js +1134 -932
  42. package/dist/server/modules/floor-plan/index.d.ts +4 -0
  43. package/dist/server/modules/floor-plan/index.js +240 -98
  44. package/dist/server/modules/menu/index.js +48 -23
  45. package/dist/server/modules/order/index.d.ts +17 -7
  46. package/dist/server/modules/order/index.js +419 -207
  47. package/dist/server/modules/products/index.d.ts +10 -4
  48. package/dist/server/modules/products/index.js +177 -84
  49. package/dist/server/modules/resource/index.js +21 -13
  50. package/dist/server/utils/product.d.ts +1 -1
  51. package/dist/server/utils/product.js +186 -22
  52. package/dist/server/utils/small-ticket.d.ts +9 -1
  53. package/dist/server/utils/small-ticket.js +131 -84
  54. package/dist/solution/BaseSales/index.d.ts +26 -3
  55. package/dist/solution/BaseSales/index.js +1384 -905
  56. package/dist/solution/BaseSales/types.d.ts +2 -0
  57. package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.d.ts +3 -0
  58. package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +10 -3
  59. package/dist/solution/BaseSales/utils.js +6 -3
  60. package/dist/solution/BookingByStep/index.d.ts +4 -4
  61. package/dist/solution/BookingTicket/index.d.ts +1 -1
  62. package/dist/solution/BookingTicket/index.js +4 -2
  63. package/dist/solution/BookingTicket/utils/addProductDecision.d.ts +2 -1
  64. package/dist/solution/BookingTicket/utils/addProductDecision.js +6 -3
  65. package/dist/solution/BookingTicket/utils/orderCustomer.js +3 -2
  66. package/dist/solution/RegisterAndLogin/config.d.ts +9 -3
  67. package/dist/solution/RegisterAndLogin/config.js +95 -40
  68. package/dist/solution/RegisterAndLogin/index.js +4 -1
  69. package/dist/solution/ScanOrder/index.d.ts +1 -0
  70. package/dist/solution/ScanOrder/index.js +31 -27
  71. package/dist/solution/ScanOrder/types.d.ts +2 -0
  72. package/dist/solution/ScanOrder/utils.d.ts +1 -0
  73. package/dist/solution/ScanOrder/utils.js +8 -3
  74. package/dist/solution/UnifiedBookingSales/index.d.ts +128 -74
  75. package/dist/solution/UnifiedBookingSales/index.js +3877 -963
  76. package/dist/solution/UnifiedBookingSales/remoteOccupancyCache.d.ts +49 -0
  77. package/dist/solution/UnifiedBookingSales/remoteOccupancyCache.js +430 -0
  78. package/dist/solution/UnifiedBookingSales/types.d.ts +161 -77
  79. package/dist/solution/UnifiedBookingSales/types.js +4 -9
  80. package/dist/solution/VenueBooking/index.d.ts +3 -0
  81. package/dist/solution/VenueBooking/index.js +549 -497
  82. package/dist/solution/VenueBooking/utils/dateSummary.d.ts +1 -0
  83. package/dist/solution/VenueBooking/utils/dateSummary.js +3 -1
  84. package/dist/solution/VenueBooking/utils/slotMerge.d.ts +2 -0
  85. package/dist/solution/VenueBooking/utils/slotMerge.js +3 -1
  86. package/dist/utils/platform.d.ts +8 -0
  87. package/dist/utils/platform.js +15 -0
  88. package/lib/core/index.d.ts +7 -0
  89. package/lib/core/index.js +20 -2
  90. package/lib/model/strategy/adapter/itemRule/adapter.d.ts +4 -1
  91. package/lib/model/strategy/adapter/itemRule/adapter.js +73 -4
  92. package/lib/model/strategy/adapter/itemRule/evaluator.d.ts +8 -1
  93. package/lib/model/strategy/adapter/itemRule/evaluator.js +23 -1
  94. package/lib/model/strategy/adapter/itemRule/type.d.ts +44 -0
  95. package/lib/model/strategy/adapter/itemRule/type.js +19 -1
  96. package/lib/modules/BookingContext/index.d.ts +3 -0
  97. package/lib/modules/BookingContext/index.js +35 -8
  98. package/lib/modules/BookingContext/types.d.ts +4 -4
  99. package/lib/modules/BookingContext/types.js +3 -3
  100. package/lib/modules/OpenData/types.d.ts +1 -0
  101. package/lib/modules/Order/index.d.ts +18 -3
  102. package/lib/modules/Order/index.js +325 -28
  103. package/lib/modules/Order/salesNotes.d.ts +31 -0
  104. package/lib/modules/Order/salesNotes.js +382 -0
  105. package/lib/modules/Order/types.d.ts +93 -5
  106. package/lib/modules/Order/types.js +4 -0
  107. package/lib/modules/Order/utils.d.ts +12 -0
  108. package/lib/modules/Order/utils.js +85 -16
  109. package/lib/modules/Product/types.d.ts +2 -0
  110. package/lib/modules/ProductList/clientDataVariants.d.ts +50 -0
  111. package/lib/modules/ProductList/clientDataVariants.js +204 -0
  112. package/lib/modules/ProductList/index.d.ts +18 -3
  113. package/lib/modules/ProductList/index.js +137 -8
  114. package/lib/modules/ProductList/types.d.ts +10 -0
  115. package/lib/modules/Quotation/index.d.ts +22 -0
  116. package/lib/modules/Quotation/index.js +158 -11
  117. package/lib/modules/ResourcePlanner/index.d.ts +0 -22
  118. package/lib/modules/ResourcePlanner/index.js +9 -129
  119. package/lib/modules/ResourcePlanner/localClock.d.ts +33 -0
  120. package/lib/modules/ResourcePlanner/localClock.js +205 -0
  121. package/lib/modules/ResourcePlanner/planner.d.ts +5 -14
  122. package/lib/modules/ResourcePlanner/planner.js +1807 -783
  123. package/lib/modules/ResourcePlanner/types.d.ts +359 -187
  124. package/lib/modules/ResourcePlanner/types.js +13 -1
  125. package/lib/plugins/request.d.ts +1 -0
  126. package/lib/server/index.d.ts +17 -2
  127. package/lib/server/index.js +149 -21
  128. package/lib/server/modules/floor-plan/index.d.ts +4 -0
  129. package/lib/server/modules/floor-plan/index.js +60 -7
  130. package/lib/server/modules/menu/index.js +26 -4
  131. package/lib/server/modules/order/index.d.ts +17 -7
  132. package/lib/server/modules/order/index.js +194 -57
  133. package/lib/server/modules/products/index.d.ts +10 -4
  134. package/lib/server/modules/products/index.js +93 -23
  135. package/lib/server/modules/resource/index.js +12 -2
  136. package/lib/server/utils/product.d.ts +1 -1
  137. package/lib/server/utils/product.js +163 -3
  138. package/lib/server/utils/small-ticket.d.ts +9 -1
  139. package/lib/server/utils/small-ticket.js +123 -79
  140. package/lib/solution/BaseSales/index.d.ts +26 -3
  141. package/lib/solution/BaseSales/index.js +396 -70
  142. package/lib/solution/BaseSales/types.d.ts +2 -0
  143. package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.d.ts +3 -0
  144. package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +9 -2
  145. package/lib/solution/BaseSales/utils.js +3 -0
  146. package/lib/solution/BookingByStep/index.d.ts +4 -4
  147. package/lib/solution/BookingTicket/index.d.ts +1 -1
  148. package/lib/solution/BookingTicket/index.js +2 -1
  149. package/lib/solution/BookingTicket/utils/addProductDecision.d.ts +2 -1
  150. package/lib/solution/BookingTicket/utils/addProductDecision.js +5 -2
  151. package/lib/solution/BookingTicket/utils/orderCustomer.js +2 -1
  152. package/lib/solution/RegisterAndLogin/config.d.ts +9 -3
  153. package/lib/solution/RegisterAndLogin/config.js +49 -8
  154. package/lib/solution/RegisterAndLogin/index.js +4 -1
  155. package/lib/solution/ScanOrder/index.d.ts +1 -0
  156. package/lib/solution/ScanOrder/index.js +5 -1
  157. package/lib/solution/ScanOrder/types.d.ts +2 -0
  158. package/lib/solution/ScanOrder/utils.d.ts +1 -0
  159. package/lib/solution/ScanOrder/utils.js +4 -0
  160. package/lib/solution/UnifiedBookingSales/index.d.ts +128 -74
  161. package/lib/solution/UnifiedBookingSales/index.js +2592 -424
  162. package/lib/solution/UnifiedBookingSales/remoteOccupancyCache.d.ts +49 -0
  163. package/lib/solution/UnifiedBookingSales/remoteOccupancyCache.js +316 -0
  164. package/lib/solution/UnifiedBookingSales/types.d.ts +161 -77
  165. package/lib/solution/UnifiedBookingSales/types.js +4 -6
  166. package/lib/solution/VenueBooking/index.d.ts +3 -0
  167. package/lib/solution/VenueBooking/index.js +33 -9
  168. package/lib/solution/VenueBooking/utils/dateSummary.d.ts +1 -0
  169. package/lib/solution/VenueBooking/utils/dateSummary.js +3 -1
  170. package/lib/solution/VenueBooking/utils/slotMerge.d.ts +2 -0
  171. package/lib/solution/VenueBooking/utils/slotMerge.js +3 -1
  172. package/lib/utils/platform.d.ts +8 -0
  173. package/lib/utils/platform.js +22 -0
  174. package/package.json +1 -1
@@ -17,6 +17,7 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
17
17
  const INDEXDB_STORE_NAME = 'orders';
18
18
  const ORDER_STORAGE_KEY_FIELD = '__order_storage_key';
19
19
  const ORDER_LAST_FULL_FETCH_AT_STORAGE_KEY = 'server_order_last_full_fetch_at';
20
+ const ORDER_LAST_FULL_FETCH_SHOP_ID_STORAGE_KEY = 'server_order_last_full_fetch_shop_id';
20
21
  const ORDER_SYNC_THROTTLE_MS_STORAGE_KEY = 'order_sync_throttle_ms';
21
22
  const DEFAULT_ORDER_SYNC_THROTTLE_MS = 2_000;
22
23
  /** pubsub 回声写入去重窗口:业务 API 刚写入后,跳过同单 SQLite patch */
@@ -182,19 +183,25 @@ class OrderModule extends _BaseModule.BaseModule {
182
183
  * 仅记录计数与订单标识,不输出商品、预约、支付或客户明细。
183
184
  */
184
185
  normalizeOrderCollectionsForIngress(order, source) {
185
- const sourceOrder = (0, _lodashEs.cloneDeep)(order);
186
- const sourceOrderRecord = sourceOrder;
186
+ const sourceOrderRecord = order;
187
+ let sourceOrder = sourceOrderRecord;
187
188
  if (Array.isArray(sourceOrderRecord.payments)) {
188
- sourceOrderRecord.payments = sourceOrderRecord.payments.map(payment => {
189
- if (!(0, _paymentUtils.isPendingOrderPayment)(payment) || (0, _paymentUtils.getOrderPaymentIdentityKeys)(payment).some(key => key.startsWith('unique_payment_number:'))) {
190
- return payment;
191
- }
192
- return {
193
- ...payment,
194
- [PENDING_PAYMENT_WITHOUT_UNIQUE_MARKER]: true
195
- };
196
- });
189
+ sourceOrder = {
190
+ ...sourceOrderRecord,
191
+ payments: sourceOrderRecord.payments.map(payment => {
192
+ if (!(0, _paymentUtils.isPendingOrderPayment)(payment) || (0, _paymentUtils.getOrderPaymentIdentityKeys)(payment).some(key => key.startsWith('unique_payment_number:'))) {
193
+ return payment;
194
+ }
195
+ return {
196
+ ...payment,
197
+ [PENDING_PAYMENT_WITHOUT_UNIQUE_MARKER]: true
198
+ };
199
+ })
200
+ };
197
201
  }
202
+
203
+ // normalizeOrderCollections 内部已经 cloneDeep;这里只浅复制被改写的 payments,
204
+ // 既不修改调用方对象,也避免对整笔订单重复深拷贝。
198
205
  const result = (0, _orderCollectionIdentity.normalizeOrderCollections)(sourceOrder);
199
206
  const normalizedOrderRecord = result.order;
200
207
  if (Array.isArray(normalizedOrderRecord.payments)) {
@@ -528,27 +535,68 @@ class OrderModule extends _BaseModule.BaseModule {
528
535
  return DEFAULT_ORDER_SYNC_THROTTLE_MS;
529
536
  }
530
537
  async preload() {
531
- // const hasPulledInCurrentWindow = this.hasPulledOrdersToday()
532
- // // 仅当缓存属于当前统计窗口时才直接复用,避免跨天/跨营业日窗口继续使用旧数据
533
- // if (cachedOrders.length > 0 && hasPulledInCurrentWindow) {
534
- // this.store.list = cloneDeep(cachedOrders)
535
- // this.syncOrdersMap()
536
- // this.core.effects.emit(OrderHooks.onOrdersChanged, this.store.list)
537
- // return
538
- // }
539
-
540
- // if (hasPulledInCurrentWindow) {
541
- // this.logInfo('当天已完成全量订单拉取,跳过本次全量拉取', {
542
- // lastFullFetchAt: this.getStorageItem(ORDER_LAST_FULL_FETCH_AT_STORAGE_KEY),
543
- // })
544
- // return
545
- // }
538
+ const preloadStartedAt = Date.now();
539
+ let cachedOrders = null;
540
+ let sqliteLoadTimings;
541
+ if (this.dbManager) {
542
+ try {
543
+ cachedOrders = await this.loadOrdersFromSQLite({
544
+ throwOnError: true,
545
+ onTiming: timings => {
546
+ sqliteLoadTimings = timings;
547
+ }
548
+ });
549
+ } catch (error) {
550
+ this.logWarning('preload-SQLite读取失败,回退远端全量拉取', {
551
+ error: error instanceof Error ? error.message : String(error)
552
+ });
553
+ }
554
+ }
555
+ if (cachedOrders !== null && this.hasPulledOrdersToday()) {
556
+ const memoryPrepareStartedAt = performance.now();
557
+ const memoryOrders = this.prepareNormalizedOrdersForMemoryList(cachedOrders);
558
+ const memoryPrepareMs = +(performance.now() - memoryPrepareStartedAt).toFixed(2);
546
559
 
547
- const orders = await this.loadOrdersByServer();
560
+ // loadOrdersFromSQLite 已返回独立且完成集合规范化的新对象;直接进入 store,
561
+ // 不再进行第二轮 normalize 和没有隔离收益的最终 cloneDeep。
562
+ this.store.list = memoryOrders;
563
+ const syncMapStartedAt = performance.now();
564
+ this.syncOrdersMap();
565
+ const syncMapMs = +(performance.now() - syncMapStartedAt).toFixed(2);
566
+ const onOrdersLoadedStartedAt = performance.now();
567
+ await this.core.effects.emit(_types.OrderHooks.onOrdersLoaded, this.store.list);
568
+ const onOrdersLoadedMs = +(performance.now() - onOrdersLoadedStartedAt).toFixed(2);
569
+ const emitOrdersChangedStartedAt = performance.now();
570
+ await this.emitOrdersChanged([], 'preload.sqlite');
571
+ const emitOrdersChangedMs = +(performance.now() - emitOrdersChangedStartedAt).toFixed(2);
572
+ this.logInfo('预加载完成', {
573
+ trigger: 'preload',
574
+ source: 'SQLite',
575
+ orderCount: this.store.list.length,
576
+ duration: `${Date.now() - preloadStartedAt}ms`,
577
+ stages: {
578
+ ...(sqliteLoadTimings || {}),
579
+ memoryPrepareMs,
580
+ syncMapMs,
581
+ onOrdersLoadedMs,
582
+ emitOrdersChangedMs
583
+ }
584
+ });
585
+ return;
586
+ }
587
+ const orders = await this.loadOrdersByServer({
588
+ trigger: 'preload'
589
+ });
548
590
  this.store.list = (0, _lodashEs.cloneDeep)(orders);
549
591
  this.syncOrdersMap();
550
592
  await this.emitOrdersChanged([], 'preload');
551
593
  await this.core.effects.emit(_types.OrderHooks.onOrdersSyncCompleted, null);
594
+ this.logInfo('预加载完成', {
595
+ trigger: 'preload',
596
+ source: 'Server',
597
+ orderCount: this.store.list.length,
598
+ duration: `${Date.now() - preloadStartedAt}ms`
599
+ });
552
600
  }
553
601
  getOrders() {
554
602
  return this.store.list;
@@ -764,10 +812,13 @@ class OrderModule extends _BaseModule.BaseModule {
764
812
  previousPayment
765
813
  };
766
814
  }
767
- async loadOrdersByServer() {
815
+ async loadOrdersByServer(options = {}) {
768
816
  let orderList = [];
817
+ let hasFetchedRemoteSnapshot = false;
818
+ const trigger = options.trigger || 'unknown';
769
819
  const protectedOrdersBeforeRemoteFetch = (await this.loadOrdersFromSQLite()).filter(order => this.shouldProtectLocalOrderFromRemoteSnapshot(order));
770
820
  this.logInfo('loadOrdersByServer-开始', {
821
+ trigger,
771
822
  hasOrderDataSource: !!this.orderDataSource,
772
823
  query: this.store?.createdAtQuery || null,
773
824
  protectedLocalOrderCount: protectedOrdersBeforeRemoteFetch.length,
@@ -782,18 +833,29 @@ class OrderModule extends _BaseModule.BaseModule {
782
833
  });
783
834
  orderList = data || [];
784
835
  this.logInfo('loadOrdersByServer-SSE拉取成功', {
836
+ trigger,
785
837
  count: orderList.length
786
838
  });
787
- this.markOrderPulledAtNow();
839
+ hasFetchedRemoteSnapshot = true;
788
840
  this.lastServerFullFetchAtMs = Date.now();
789
841
  } catch {
790
842
  this.logInfo('loadOrdersByServer-SSE拉取失败,保持当前内存订单');
791
843
  return this.store.list;
792
844
  }
793
845
  }
794
- const mergedOrders = await this.replaceOrdersSnapshotInSQLite(orderList, 'loadOrdersByServer', protectedOrdersBeforeRemoteFetch);
846
+ if (hasFetchedRemoteSnapshot) {
847
+ this.clearOrderSnapshotMarker();
848
+ }
849
+ const {
850
+ orders: mergedOrders,
851
+ persisted: isRemoteSnapshotPersisted
852
+ } = await this.replaceOrdersSnapshotInSQLite(orderList, 'loadOrdersByServer', protectedOrdersBeforeRemoteFetch);
853
+ if (hasFetchedRemoteSnapshot && isRemoteSnapshotPersisted) {
854
+ this.markOrderPulledAtNow();
855
+ }
795
856
  const memoryOrders = this.normalizeOrdersForMemoryList(mergedOrders, 'loadOrdersByServer.memory');
796
857
  this.logInfo('loadOrdersByServer-结束', {
858
+ trigger,
797
859
  count: memoryOrders.length,
798
860
  persistedCount: mergedOrders.length,
799
861
  sqliteOnlyDraftCount: mergedOrders.length - memoryOrders.length
@@ -807,22 +869,37 @@ class OrderModule extends _BaseModule.BaseModule {
807
869
  * 拿到完整数据后一次性替换 store,触发订单变更与同步完成事件
808
870
  * @returns 刷新后的订单列表
809
871
  */
810
- async silentRefresh() {
872
+ async silentRefresh(options = {}) {
811
873
  const startTime = Date.now();
874
+ const trigger = options.trigger || 'background';
812
875
  const elapsedSinceFullFetch = Date.now() - this.lastServerFullFetchAtMs;
813
876
  if (this.lastServerFullFetchAtMs > 0 && elapsedSinceFullFetch < ORDER_SILENT_REFRESH_MIN_INTERVAL_MS) {
877
+ this.logInfo('silentRefresh 跳过全量 SSE', {
878
+ trigger,
879
+ reason: 'within_min_interval',
880
+ minIntervalMs: ORDER_SILENT_REFRESH_MIN_INTERVAL_MS,
881
+ elapsedSinceFullFetchMs: elapsedSinceFullFetch,
882
+ orderCount: this.store.list.length
883
+ });
814
884
  return this.store.list;
815
885
  }
816
- this.logInfo('silentRefresh 开始');
886
+ this.logInfo('silentRefresh 开始', {
887
+ trigger,
888
+ elapsedSinceFullFetchMs: elapsedSinceFullFetch,
889
+ orderCount: this.store.list.length
890
+ });
817
891
  try {
818
892
  // 刷新前,需要更新当前营业日边界(store.createdAtQuery)
819
893
  this.updateCreateStore();
820
- const orders = await this.loadOrdersByServer();
894
+ const orders = await this.loadOrdersByServer({
895
+ trigger
896
+ });
821
897
  this.store.list = (0, _lodashEs.cloneDeep)(orders);
822
898
  this.syncOrdersMap();
823
899
  await this.emitOrdersChanged([], 'silentRefresh');
824
900
  await this.core.effects.emit(_types.OrderHooks.onOrdersSyncCompleted, null);
825
901
  this.logInfo('silentRefresh 完成', {
902
+ trigger,
826
903
  orderCount: orders.length,
827
904
  duration: `${Date.now() - startTime}ms`
828
905
  });
@@ -830,6 +907,7 @@ class OrderModule extends _BaseModule.BaseModule {
830
907
  } catch (error) {
831
908
  const errorMessage = error instanceof Error ? error.message : String(error);
832
909
  this.logError('silentRefresh 失败', {
910
+ trigger,
833
911
  duration: `${Date.now() - startTime}ms`,
834
912
  error: errorMessage
835
913
  });
@@ -838,6 +916,13 @@ class OrderModule extends _BaseModule.BaseModule {
838
916
  }
839
917
  normalizeOrderForMemoryList(order, source = 'normalizeOrderForMemoryList') {
840
918
  const normalizedOrder = this.normalizeOrderCollectionsForIngress(order, source);
919
+ return this.prepareNormalizedOrderForMemoryList(normalizedOrder);
920
+ }
921
+
922
+ /**
923
+ * 将已经完成集合规范化的订单转换为内存身份,不重复扫描 products/bookings/payments。
924
+ */
925
+ prepareNormalizedOrderForMemoryList(normalizedOrder) {
841
926
  const externalSaleNumber = normalizedOrder?.external_sale_number;
842
927
  if (this.isDraftOrder(normalizedOrder) && !this.hasRealOrderId(normalizedOrder)) {
843
928
  return normalizedOrder;
@@ -853,6 +938,9 @@ class OrderModule extends _BaseModule.BaseModule {
853
938
  order_id: externalSaleNumber
854
939
  };
855
940
  }
941
+ prepareNormalizedOrdersForMemoryList(orders) {
942
+ return orders.map(order => this.prepareNormalizedOrderForMemoryList(order)).filter(order => this.shouldKeepOrderInMemory(order));
943
+ }
856
944
  normalizeOrdersForMemoryList(orders, source) {
857
945
  return orders.map(order => this.normalizeOrderForMemoryList(order, source)).filter(order => this.shouldKeepOrderInMemory(order));
858
946
  }
@@ -2021,22 +2109,29 @@ class OrderModule extends _BaseModule.BaseModule {
2021
2109
  }
2022
2110
 
2023
2111
  /**
2024
- * 当前订单拉取窗口:
2025
- * - 优先使用 createdAtQuery.sales_time_between(已按营业日边界初始化)
2026
- * - 无法解析时退化为自然日窗口
2112
+ * 当前订单缓存可复用窗口。
2113
+ * start_time 模式使用精确的营业日起止时间;配置缺失或异常时退化为自然日。
2027
2114
  */
2028
- getCurrentPullWindow() {
2115
+ getCurrentOrderCacheWindow() {
2029
2116
  const now = (0, _dayjs.default)();
2030
- const range = this.store?.createdAtQuery?.sales_time_between;
2031
- if (Array.isArray(range) && range.length >= 2) {
2032
- const startAt = (0, _dayjs.default)(range[0]);
2033
- const endExclusiveAt = (0, _dayjs.default)(range[1]);
2034
- if (startAt.isValid() && endExclusiveAt.isValid() && endExclusiveAt.isAfter(startAt)) {
2035
- return {
2036
- startAt,
2037
- endExclusiveAt
2038
- };
2117
+ try {
2118
+ const appPlugin = this.core.getPlugin('app');
2119
+ const app = appPlugin?.getApp();
2120
+ const coreData = app?.data.store.getStore().getDataByModel('core');
2121
+ const boundary = coreData?.core?.operating_day_boundary;
2122
+ if (boundary?.type === 'start_time') {
2123
+ const boundaryTime = String(boundary.time || '').trim();
2124
+ const todayBoundary = (0, _dayjs.default)(`${now.format('YYYY-MM-DD')} ${boundaryTime}`);
2125
+ if (todayBoundary.isValid()) {
2126
+ const startAt = now.isBefore(todayBoundary) ? todayBoundary.subtract(1, 'day') : todayBoundary;
2127
+ return {
2128
+ startAt,
2129
+ endExclusiveAt: startAt.add(1, 'day')
2130
+ };
2131
+ }
2039
2132
  }
2133
+ } catch {
2134
+ // 读取营业日配置失败时按自然日兜底,不影响启动。
2040
2135
  }
2041
2136
  const startAt = now.startOf('day');
2042
2137
  return {
@@ -2047,19 +2142,41 @@ class OrderModule extends _BaseModule.BaseModule {
2047
2142
  hasPulledOrdersToday() {
2048
2143
  const lastFullFetchAt = this.getStorageItem(ORDER_LAST_FULL_FETCH_AT_STORAGE_KEY);
2049
2144
  if (!lastFullFetchAt) return false;
2145
+ const currentShopId = this.getCurrentShopId();
2146
+ const lastFullFetchShopId = this.getStorageItem(ORDER_LAST_FULL_FETCH_SHOP_ID_STORAGE_KEY);
2147
+ if (!currentShopId || !lastFullFetchShopId || currentShopId !== lastFullFetchShopId) {
2148
+ return false;
2149
+ }
2050
2150
  const parsed = (0, _dayjs.default)(lastFullFetchAt);
2051
2151
  if (!parsed.isValid()) return false;
2052
-
2053
- // 按当前拉取窗口判断,而不是自然日判断。
2054
- // 这样在营业日跨日边界(start_time)场景下也能正确判定是否需要重新拉取。
2055
2152
  const {
2056
2153
  startAt,
2057
2154
  endExclusiveAt
2058
- } = this.getCurrentPullWindow();
2155
+ } = this.getCurrentOrderCacheWindow();
2059
2156
  return (parsed.isAfter(startAt) || parsed.isSame(startAt)) && parsed.isBefore(endExclusiveAt);
2060
2157
  }
2061
2158
  markOrderPulledAtNow() {
2159
+ const currentShopId = this.getCurrentShopId();
2160
+ if (!currentShopId) return;
2062
2161
  this.setStorageItem(ORDER_LAST_FULL_FETCH_AT_STORAGE_KEY, (0, _dayjs.default)().format('YYYY-MM-DD HH:mm:ss'));
2162
+ this.setStorageItem(ORDER_LAST_FULL_FETCH_SHOP_ID_STORAGE_KEY, currentShopId);
2163
+ }
2164
+ clearOrderSnapshotMarker() {
2165
+ this.setStorageItem(ORDER_LAST_FULL_FETCH_AT_STORAGE_KEY, '');
2166
+ this.setStorageItem(ORDER_LAST_FULL_FETCH_SHOP_ID_STORAGE_KEY, '');
2167
+ }
2168
+ getCurrentShopId() {
2169
+ try {
2170
+ const appPlugin = this.core.getPlugin('app');
2171
+ const app = appPlugin?.getApp();
2172
+ if (!app) return null;
2173
+ const coreData = app.data.store.getStore().getDataByModel('core');
2174
+ const shopId = coreData?.core?.id;
2175
+ if (shopId === undefined || shopId === null || shopId === '') return null;
2176
+ return String(shopId);
2177
+ } catch {
2178
+ return null;
2179
+ }
2063
2180
  }
2064
2181
  getStorageItem(key) {
2065
2182
  try {
@@ -2085,12 +2202,24 @@ class OrderModule extends _BaseModule.BaseModule {
2085
2202
  // 忽略存储异常,避免影响主流程
2086
2203
  }
2087
2204
  }
2088
- async loadOrdersFromSQLite() {
2205
+ async loadOrdersFromSQLite(options = {}) {
2089
2206
  if (!this.dbManager) return [];
2090
2207
  try {
2208
+ const loadStartedAt = performance.now();
2209
+ const rawReadStartedAt = performance.now();
2091
2210
  const orders = await this.dbManager.getAll(INDEXDB_STORE_NAME);
2092
- return (orders || []).map(order => this.normalizeOrderCollectionsForIngress(order, 'loadOrdersFromSQLite'));
2093
- } catch {
2211
+ const sqliteRawReadMs = +(performance.now() - rawReadStartedAt).toFixed(2);
2212
+ const normalizeStartedAt = performance.now();
2213
+ const normalizedOrders = (orders || []).map(order => this.normalizeOrderCollectionsForIngress(order, 'loadOrdersFromSQLite'));
2214
+ const sqliteNormalizeMs = +(performance.now() - normalizeStartedAt).toFixed(2);
2215
+ options.onTiming?.({
2216
+ sqliteRawReadMs,
2217
+ sqliteNormalizeMs,
2218
+ sqliteLoadTotalMs: +(performance.now() - loadStartedAt).toFixed(2)
2219
+ });
2220
+ return normalizedOrders;
2221
+ } catch (error) {
2222
+ if (options.throwOnError) throw error;
2094
2223
  return [];
2095
2224
  }
2096
2225
  }
@@ -2323,14 +2452,18 @@ class OrderModule extends _BaseModule.BaseModule {
2323
2452
  * 用于 SSE 全量拉取与营业日窗口裁剪。
2324
2453
  *
2325
2454
  * @example
2326
- * await this.replaceOrdersSnapshotInSQLite(orderList)
2455
+ * const { orders, persisted } = await this.replaceOrdersSnapshotInSQLite(orderList, 'refresh')
2327
2456
  */
2328
2457
  async replaceOrdersSnapshotInSQLite(orderList, source, protectedOrdersBeforeRemoteFetch = []) {
2329
2458
  if (!this.dbManager) {
2330
- return this.compactOrderListByIdentity(orderList.map(order => this.normalizeRemoteSyncedOrder(order)), `${source}.snapshotNoDb`).list;
2459
+ return {
2460
+ orders: this.compactOrderListByIdentity(orderList.map(order => this.normalizeRemoteSyncedOrder(order)), `${source}.snapshotNoDb`).list,
2461
+ persisted: false
2462
+ };
2331
2463
  }
2332
2464
  const remoteSnapshot = (0, _lodashEs.cloneDeep)(orderList).map(order => this.normalizeOrderCollectionsForIngress(order, `${source}.remoteIngress`));
2333
2465
  let mergedSnapshot = this.compactOrderListByIdentity(remoteSnapshot.map(order => this.normalizeRemoteSyncedOrder(order)), `${source}.remoteSnapshot`).list;
2466
+ let persisted = false;
2334
2467
  try {
2335
2468
  await this.runInOrderSQLiteSaveQueue(async () => {
2336
2469
  const existingOrders = typeof this.dbManager.getAll === 'function' ? await this.dbManager.getAll(INDEXDB_STORE_NAME) : [];
@@ -2379,13 +2512,17 @@ class OrderModule extends _BaseModule.BaseModule {
2379
2512
  count: mergedSnapshot.length
2380
2513
  });
2381
2514
  });
2515
+ persisted = true;
2382
2516
  } catch (error) {
2383
2517
  this.logError('全量保存订单到 SQLite 失败', {
2384
2518
  error: error instanceof Error ? error.message : String(error),
2385
2519
  orderList: remoteSnapshot.length
2386
2520
  });
2387
2521
  }
2388
- return mergedSnapshot;
2522
+ return {
2523
+ orders: mergedSnapshot,
2524
+ persisted
2525
+ };
2389
2526
  }
2390
2527
 
2391
2528
  /**
@@ -2402,7 +2539,7 @@ class OrderModule extends _BaseModule.BaseModule {
2402
2539
  await this.dbManager.clear(INDEXDB_STORE_NAME);
2403
2540
  });
2404
2541
  }
2405
- this.setStorageItem(ORDER_LAST_FULL_FETCH_AT_STORAGE_KEY, '');
2542
+ this.clearOrderSnapshotMarker();
2406
2543
  this.emitOrdersChanged([], 'clear');
2407
2544
  }
2408
2545
  }
@@ -25,6 +25,7 @@ export declare class ProductsModule extends BaseModule implements Module {
25
25
  private quotationBridgeLoadedKey?;
26
26
  private quotationBridgeRefreshPromise?;
27
27
  private quotationBridgeRefreshKey?;
28
+ private quotationBridgeUpdatedCallback?;
28
29
  private quotationScheduleCache;
29
30
  private quotationScheduleCacheSource;
30
31
  private quotationScheduleCacheLoaded;
@@ -72,6 +73,7 @@ export declare class ProductsModule extends BaseModule implements Module {
72
73
  scheduleModule?: any;
73
74
  channel?: string;
74
75
  customer_id?: number | string;
76
+ onQuotationUpdated?: () => void;
75
77
  }): Promise<void>;
76
78
  /**
77
79
  * 刷新桥接器内的报价单列表。
@@ -87,8 +89,8 @@ export declare class ProductsModule extends BaseModule implements Module {
87
89
  private buildBundleGroupPriceData;
88
90
  /**
89
91
  * 获取应用了价格的商品列表(带缓存)
90
- * 使用日期作为缓存 key,同一天的商品价格会被缓存
91
- * 缓存的是已经应用了价格的完整商品列表,避免重复转换
92
+ * 使用时间、客户、策略、商品范围和语言作为缓存 key
93
+ * 缓存的是已经应用了价格与多语言的完整商品列表,避免重复转换
92
94
  * @param schedule_date 日期
93
95
  * @param extraContext 额外的上下文数据(可选,由 Server 层传入)
94
96
  * @param options 可选参数
@@ -181,7 +183,9 @@ export declare class ProductsModule extends BaseModule implements Module {
181
183
  /**
182
184
  * 通过 ProductDataSource SSE 加载完整商品列表
183
185
  */
184
- loadProductsByServer(): Promise<any>;
186
+ loadProductsByServer(options?: {
187
+ trigger?: string;
188
+ }): Promise<any>;
185
189
  /**
186
190
  * 纯请求方法:通过 HTTP 接口获取商品列表(无副作用,不触发事件、不写 IndexDB)
187
191
  * @param params 查询参数
@@ -331,7 +335,9 @@ export declare class ProductsModule extends BaseModule implements Module {
331
335
  * 拿到完整数据后一次性替换 store,清除价格缓存,触发 onProductsSyncCompleted
332
336
  * @returns 刷新后的商品列表
333
337
  */
334
- silentRefresh(): Promise<ProductData[]>;
338
+ silentRefresh(options?: {
339
+ trigger?: string;
340
+ }): Promise<ProductData[]>;
335
341
  /**
336
342
  * 销毁同步资源(取消 pubsub 订阅、清除定时器)
337
343
  */