@pisell/pisellos 2.2.274 → 2.2.276

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 (117) hide show
  1. package/dist/core/index.d.ts +2 -0
  2. package/dist/core/index.js +7 -0
  3. package/dist/model/strategy/adapter/promotion/adapter.d.ts +4 -0
  4. package/dist/model/strategy/adapter/promotion/adapter.js +23 -0
  5. package/dist/model/strategy/adapter/promotion/evaluator.d.ts +17 -0
  6. package/dist/model/strategy/adapter/promotion/evaluator.js +279 -6
  7. package/dist/model/strategy/adapter/promotion/examples.d.ts +86 -0
  8. package/dist/model/strategy/adapter/promotion/examples.js +99 -0
  9. package/dist/model/strategy/adapter/promotion/index.d.ts +1 -1
  10. package/dist/model/strategy/adapter/promotion/index.js +1 -1
  11. package/dist/model/strategy/adapter/promotion/type.d.ts +90 -2
  12. package/dist/model/strategy/adapter/promotion/type.js +45 -0
  13. package/dist/modules/OpenData/index.d.ts +2 -0
  14. package/dist/modules/OpenData/index.js +8 -0
  15. package/dist/modules/Order/index.d.ts +28 -8
  16. package/dist/modules/Order/index.js +907 -588
  17. package/dist/modules/Order/payment-utils.d.ts +26 -0
  18. package/dist/modules/Order/payment-utils.js +225 -0
  19. package/dist/modules/Order/types.d.ts +35 -1
  20. package/dist/modules/Order/utils/orderCollectionIdentity.js +7 -2
  21. package/dist/modules/Order/utils.js +10 -6
  22. package/dist/modules/Payment/index.d.ts +2 -0
  23. package/dist/modules/Payment/index.js +78 -49
  24. package/dist/modules/Payment/types.d.ts +104 -25
  25. package/dist/modules/Payment/types.js +17 -0
  26. package/dist/modules/Payment/walletpass.d.ts +38 -1
  27. package/dist/modules/Payment/walletpass.js +917 -114
  28. package/dist/modules/Rules/index.d.ts +3 -0
  29. package/dist/modules/Rules/index.js +100 -70
  30. package/dist/server/index.d.ts +25 -0
  31. package/dist/server/index.js +1993 -1061
  32. package/dist/server/modules/index.d.ts +1 -1
  33. package/dist/server/modules/order/index.d.ts +70 -4
  34. package/dist/server/modules/order/index.js +1816 -728
  35. package/dist/server/modules/order/types.d.ts +25 -3
  36. package/dist/server/modules/order/types.js +7 -1
  37. package/dist/solution/BaseSales/index.d.ts +104 -5
  38. package/dist/solution/BaseSales/index.js +1767 -436
  39. package/dist/solution/BaseSales/types.d.ts +55 -1
  40. package/dist/solution/BaseSales/types.js +2 -1
  41. package/dist/solution/BaseSales/utils/cartPromotion.d.ts +3 -1
  42. package/dist/solution/BaseSales/utils/cartPromotion.js +94 -32
  43. package/dist/solution/BaseSales/utils/parseSalesResponse.d.ts +6 -1
  44. package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +7 -4
  45. package/dist/solution/BookingByStep/index.d.ts +1 -1
  46. package/dist/solution/BookingTicket/index.d.ts +21 -5
  47. package/dist/solution/BookingTicket/index.js +139 -56
  48. package/dist/solution/BookingTicket/types.d.ts +1 -0
  49. package/dist/solution/BookingTicket/utils/scan/applyGlobalScan.d.ts +8 -0
  50. package/dist/solution/BookingTicket/utils/scan/applyGlobalScan.js +173 -83
  51. package/dist/solution/Sales/index.d.ts +2 -0
  52. package/dist/solution/Sales/index.js +26 -4
  53. package/dist/solution/ScanOrder/index.js +31 -20
  54. package/dist/solution/VenueBooking/index.d.ts +1 -0
  55. package/dist/solution/VenueBooking/index.js +30 -19
  56. package/dist/types/index.d.ts +1 -0
  57. package/dist/utils/payment-number.d.ts +9 -0
  58. package/dist/utils/payment-number.js +69 -0
  59. package/lib/core/index.d.ts +2 -0
  60. package/lib/core/index.js +4 -0
  61. package/lib/model/strategy/adapter/promotion/adapter.d.ts +4 -0
  62. package/lib/model/strategy/adapter/promotion/adapter.js +20 -0
  63. package/lib/model/strategy/adapter/promotion/evaluator.d.ts +17 -0
  64. package/lib/model/strategy/adapter/promotion/evaluator.js +235 -0
  65. package/lib/model/strategy/adapter/promotion/examples.d.ts +86 -0
  66. package/lib/model/strategy/adapter/promotion/examples.js +91 -0
  67. package/lib/model/strategy/adapter/promotion/index.d.ts +1 -1
  68. package/lib/model/strategy/adapter/promotion/index.js +2 -0
  69. package/lib/model/strategy/adapter/promotion/type.d.ts +90 -2
  70. package/lib/model/strategy/adapter/promotion/type.js +2 -0
  71. package/lib/modules/OpenData/index.d.ts +2 -0
  72. package/lib/modules/OpenData/index.js +5 -0
  73. package/lib/modules/Order/index.d.ts +28 -8
  74. package/lib/modules/Order/index.js +252 -84
  75. package/lib/modules/Order/payment-utils.d.ts +26 -0
  76. package/lib/modules/Order/payment-utils.js +224 -0
  77. package/lib/modules/Order/types.d.ts +35 -1
  78. package/lib/modules/Order/utils/orderCollectionIdentity.js +3 -0
  79. package/lib/modules/Order/utils.js +11 -4
  80. package/lib/modules/Payment/index.d.ts +2 -0
  81. package/lib/modules/Payment/index.js +33 -12
  82. package/lib/modules/Payment/types.d.ts +104 -25
  83. package/lib/modules/Payment/types.js +1 -0
  84. package/lib/modules/Payment/walletpass.d.ts +38 -1
  85. package/lib/modules/Payment/walletpass.js +730 -21
  86. package/lib/modules/Rules/index.d.ts +3 -0
  87. package/lib/modules/Rules/index.js +38 -9
  88. package/lib/server/index.d.ts +25 -0
  89. package/lib/server/index.js +726 -31
  90. package/lib/server/modules/index.d.ts +1 -1
  91. package/lib/server/modules/order/index.d.ts +70 -4
  92. package/lib/server/modules/order/index.js +818 -69
  93. package/lib/server/modules/order/types.d.ts +25 -3
  94. package/lib/server/modules/order/types.js +1 -0
  95. package/lib/solution/BaseSales/index.d.ts +104 -5
  96. package/lib/solution/BaseSales/index.js +858 -27
  97. package/lib/solution/BaseSales/types.d.ts +55 -1
  98. package/lib/solution/BaseSales/types.js +2 -1
  99. package/lib/solution/BaseSales/utils/cartPromotion.d.ts +3 -1
  100. package/lib/solution/BaseSales/utils/cartPromotion.js +66 -12
  101. package/lib/solution/BaseSales/utils/parseSalesResponse.d.ts +6 -1
  102. package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +4 -3
  103. package/lib/solution/BookingByStep/index.d.ts +1 -1
  104. package/lib/solution/BookingTicket/index.d.ts +21 -5
  105. package/lib/solution/BookingTicket/index.js +64 -13
  106. package/lib/solution/BookingTicket/types.d.ts +1 -0
  107. package/lib/solution/BookingTicket/utils/scan/applyGlobalScan.d.ts +8 -0
  108. package/lib/solution/BookingTicket/utils/scan/applyGlobalScan.js +37 -5
  109. package/lib/solution/Sales/index.d.ts +2 -0
  110. package/lib/solution/Sales/index.js +20 -6
  111. package/lib/solution/ScanOrder/index.js +8 -0
  112. package/lib/solution/VenueBooking/index.d.ts +1 -0
  113. package/lib/solution/VenueBooking/index.js +8 -0
  114. package/lib/types/index.d.ts +1 -0
  115. package/lib/utils/payment-number.d.ts +9 -0
  116. package/lib/utils/payment-number.js +64 -0
  117. package/package.json +1 -1
@@ -37,6 +37,7 @@ var import_dayjs = __toESM(require("dayjs"));
37
37
  var import_BaseModule = require("../../../modules/BaseModule");
38
38
  var import_orderCollectionIdentity = require("../../../modules/Order/utils/orderCollectionIdentity");
39
39
  var import_types = require("./types");
40
+ var import_payment_utils = require("../../../modules/Order/payment-utils");
40
41
  var INDEXDB_STORE_NAME = "orders";
41
42
  var ORDER_LAST_FULL_FETCH_AT_STORAGE_KEY = "server_order_last_full_fetch_at";
42
43
  var ORDER_SYNC_THROTTLE_MS_STORAGE_KEY = "order_sync_throttle_ms";
@@ -46,10 +47,13 @@ var ORDER_SILENT_REFRESH_MIN_INTERVAL_MS = 3e4;
46
47
  var ORDER_BUSINESS_WRITE_SOURCES = /* @__PURE__ */ new Set([
47
48
  "upsertOrdersFromRemote",
48
49
  "upsertPendingSyncOrders",
50
+ "upsertLocalDraftOrders",
49
51
  "overwriteExistingOrder",
50
- "markOrderSyncedByExternalSaleNumber"
52
+ "markOrderSyncedByExternalSaleNumber",
53
+ "updateLocalPayment"
51
54
  ]);
52
55
  var ORDER_MERGE_SELF_CHECK = false;
56
+ var PENDING_PAYMENT_WITHOUT_UNIQUE_MARKER = "__pisell_os_pending_payment_without_unique_number__";
53
57
  var ORDER_AUTHORITATIVE_COLLECTION_FIELDS = ["products", "bookings", "payments"];
54
58
  var ORDER_COLLECTION_KIND_BY_FIELD = {
55
59
  products: "product",
@@ -85,6 +89,24 @@ var OrderModule = class extends import_BaseModule.BaseModule {
85
89
  };
86
90
  await this.core.effects.emit(import_types.OrderHooks.onOrdersChanged, payload);
87
91
  }
92
+ /**
93
+ * 广播远端商品成员增删。监听器异常必须与订单落库和原有订单广播隔离。
94
+ */
95
+ async emitRemoteProductMembershipChanges(changes) {
96
+ for (const payload of changes) {
97
+ try {
98
+ await this.core.effects.emit(import_types.OrderHooks.onRemoteProductMembershipChanged, payload);
99
+ } catch (error) {
100
+ this.logError("广播远端订单商品成员变更失败", {
101
+ order_id: payload.order_id,
102
+ source: payload.source,
103
+ added_product_count: payload.added_product_keys.length,
104
+ removed_product_count: payload.removed_product_keys.length,
105
+ error: error instanceof Error ? error.message : String(error)
106
+ });
107
+ }
108
+ }
109
+ }
88
110
  async initialize(core, options) {
89
111
  var _a;
90
112
  this.core = core;
@@ -100,7 +122,10 @@ var OrderModule = class extends import_BaseModule.BaseModule {
100
122
  this.updateCreateStore();
101
123
  }
102
124
  if (Array.isArray((_a = options == null ? void 0 : options.initialState) == null ? void 0 : _a.list)) {
103
- this.store.list = options.initialState.list.map((order) => this.normalizeOrderForMemoryList(order));
125
+ this.store.list = this.normalizeOrdersForMemoryList(
126
+ options.initialState.list,
127
+ "initialState"
128
+ );
104
129
  this.syncOrdersMap();
105
130
  this.emitOrdersChanged([], "initialState");
106
131
  } else {
@@ -166,7 +191,35 @@ var OrderModule = class extends import_BaseModule.BaseModule {
166
191
  * 仅记录计数与订单标识,不输出商品、预约、支付或客户明细。
167
192
  */
168
193
  normalizeOrderCollectionsForIngress(order, source) {
169
- const result = (0, import_orderCollectionIdentity.normalizeOrderCollections)(order);
194
+ const sourceOrder = (0, import_lodash_es.cloneDeep)(order);
195
+ const sourceOrderRecord = sourceOrder;
196
+ if (Array.isArray(sourceOrderRecord.payments)) {
197
+ sourceOrderRecord.payments = sourceOrderRecord.payments.map((payment) => {
198
+ if (!(0, import_payment_utils.isPendingOrderPayment)(payment) || (0, import_payment_utils.getOrderPaymentIdentityKeys)(payment).some((key) => key.startsWith("unique_payment_number:"))) {
199
+ return payment;
200
+ }
201
+ return {
202
+ ...payment,
203
+ [PENDING_PAYMENT_WITHOUT_UNIQUE_MARKER]: true
204
+ };
205
+ });
206
+ }
207
+ const result = (0, import_orderCollectionIdentity.normalizeOrderCollections)(sourceOrder);
208
+ const normalizedOrderRecord = result.order;
209
+ if (Array.isArray(normalizedOrderRecord.payments)) {
210
+ normalizedOrderRecord.payments = normalizedOrderRecord.payments.map(
211
+ (payment) => {
212
+ if (!(payment == null ? void 0 : payment[PENDING_PAYMENT_WITHOUT_UNIQUE_MARKER]))
213
+ return payment;
214
+ const nextPayment = { ...payment };
215
+ const metadata = { ...nextPayment.metadata || {} };
216
+ delete metadata.unique_payment_number;
217
+ delete nextPayment[PENDING_PAYMENT_WITHOUT_UNIQUE_MARKER];
218
+ nextPayment.metadata = metadata;
219
+ return nextPayment;
220
+ }
221
+ );
222
+ }
170
223
  const kinds = ["product", "booking", "payment"];
171
224
  const hasChanges = kinds.some((kind) => {
172
225
  const stats = result.stats[kind];
@@ -202,9 +255,16 @@ var OrderModule = class extends import_BaseModule.BaseModule {
202
255
  /**
203
256
  * incoming 决定权威成员集合;同一持久化行仍从 existing 补齐 incoming 省略的本地展示字段。
204
257
  */
205
- mergeAuthoritativeOrderCollection(field, existing, incoming, uidRemaps = []) {
258
+ mergeAuthoritativeOrderCollection(field, existing, incoming, uidRemaps = [], options = {}) {
206
259
  const kind = ORDER_COLLECTION_KIND_BY_FIELD[field];
207
260
  const existingItems = Array.isArray(existing) ? existing : [];
261
+ if (field === "payments" && options.preserveIdentifiedPendingPayments) {
262
+ return (0, import_payment_utils.mergeOrderPaymentListsByIdentity)(
263
+ existingItems,
264
+ incoming,
265
+ { preserveUnmatchedExistingWhen: import_payment_utils.isIdentifiedPendingOrderPayment }
266
+ );
267
+ }
208
268
  return incoming.map((incomingItem) => {
209
269
  const existingItem = existingItems.find(
210
270
  (item) => (0, import_orderCollectionIdentity.isSameOrderCollectionItem)(kind, item, incomingItem)
@@ -272,7 +332,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
272
332
  * @example
273
333
  * const { toWrite, skipped } = this.filterRedundantPatchOrders('pubsub.bodyUpsert', orders, mergeActions)
274
334
  */
275
- filterRedundantPatchOrders(source, orders, mergeActions) {
335
+ filterRedundantPatchOrders(source, orders, mergeActions, forceWriteStorageKeys = /* @__PURE__ */ new Set()) {
276
336
  const eligibleSkipSources = /* @__PURE__ */ new Set(["pubsub.bodyUpsert", "pubsub.httpRefresh"]);
277
337
  if (!eligibleSkipSources.has(source)) {
278
338
  return { toWrite: orders, skipped: [] };
@@ -292,6 +352,10 @@ var OrderModule = class extends import_BaseModule.BaseModule {
292
352
  toWrite.push(order);
293
353
  continue;
294
354
  }
355
+ if (forceWriteStorageKeys.has(storageKey)) {
356
+ toWrite.push(order);
357
+ continue;
358
+ }
295
359
  const recent = this.recentSqliteWriteByStorageKey.get(storageKey);
296
360
  if (recent && ORDER_BUSINESS_WRITE_SOURCES.has(recent.source) && now - recent.ts < ORDER_SQLITE_DEDUPE_MS) {
297
361
  skipped.push({
@@ -460,17 +524,11 @@ var OrderModule = class extends import_BaseModule.BaseModule {
460
524
  return DEFAULT_ORDER_SYNC_THROTTLE_MS;
461
525
  }
462
526
  async preload() {
463
- const getData = async () => {
464
- const orders = await this.loadOrdersByServer();
465
- if (orders.length === 0) {
466
- return;
467
- }
468
- this.store.list = (0, import_lodash_es.cloneDeep)(orders);
469
- this.syncOrdersMap();
470
- this.emitOrdersChanged([], "preload");
471
- this.core.effects.emit(import_types.OrderHooks.onOrdersSyncCompleted, null);
472
- };
473
- getData();
527
+ const orders = await this.loadOrdersByServer();
528
+ this.store.list = (0, import_lodash_es.cloneDeep)(orders);
529
+ this.syncOrdersMap();
530
+ await this.emitOrdersChanged([], "preload");
531
+ await this.core.effects.emit(import_types.OrderHooks.onOrdersSyncCompleted, null);
474
532
  }
475
533
  getOrders() {
476
534
  return this.store.list;
@@ -482,6 +540,27 @@ var OrderModule = class extends import_BaseModule.BaseModule {
482
540
  getOrderByOrderId(orderId) {
483
541
  return this.store.map.get(this.getIdKey(orderId));
484
542
  }
543
+ buildLocalPaymentOrderLogContext(order) {
544
+ if (!order)
545
+ return {};
546
+ return {
547
+ order_id: order.order_id ?? null,
548
+ external_sale_number: order.external_sale_number ?? null,
549
+ order_number: order.order_number ?? null,
550
+ shop_order_number: order.shop_order_number ?? null,
551
+ shop_full_order_number: order.shop_full_order_number ?? null,
552
+ order_status: order.status ?? null,
553
+ payment_status: order.payment_status ?? null,
554
+ is_draft_order: Number(order.is_draft_order || 0),
555
+ need_sync: Number(order.need_sync || 0),
556
+ platform: order.platform ?? null,
557
+ business_code: order.business_code ?? null,
558
+ order_sales_channel: order.order_sales_channel ?? null,
559
+ sales_channel: order.sales_channel ?? null,
560
+ order_type: order.type ?? null,
561
+ payments_count: Array.isArray(order.payments) ? order.payments.length : 0
562
+ };
563
+ }
485
564
  getLocalOrderFromMemoryByLookup(lookup) {
486
565
  const key = this.getIdKey(lookup);
487
566
  const memoryOrderById = this.store.map.get(key);
@@ -490,28 +569,200 @@ var OrderModule = class extends import_BaseModule.BaseModule {
490
569
  const memoryOrder = this.store.list.find((order) => this.isOrderLookupMatch(order, key));
491
570
  return memoryOrder || null;
492
571
  }
493
- async getLocalOrderByLookup(lookup) {
572
+ async getLocalOrderByLookup(lookup, options = {}) {
494
573
  const key = this.getIdKey(lookup);
574
+ const logContext = {
575
+ operation_id: options.operationId,
576
+ lookup: key,
577
+ memory_order_count: this.store.list.length
578
+ };
579
+ if (options.operationId) {
580
+ this.logInfo("getLocalOrderByLookup-localPayment-开始", logContext);
581
+ }
495
582
  const memoryOrderById = this.store.map.get(key);
496
- if (memoryOrderById)
583
+ if (memoryOrderById) {
584
+ if (options.operationId) {
585
+ this.logInfo("getLocalOrderByLookup-localPayment-命中内存映射", {
586
+ ...logContext,
587
+ ...this.buildLocalPaymentOrderLogContext(memoryOrderById)
588
+ });
589
+ }
497
590
  return memoryOrderById;
591
+ }
498
592
  const memoryOrder = this.store.list.find((order) => this.isOrderLookupMatch(order, key));
499
- if (memoryOrder)
593
+ if (memoryOrder) {
594
+ if (options.operationId) {
595
+ this.logInfo("getLocalOrderByLookup-localPayment-命中内存列表", {
596
+ ...logContext,
597
+ ...this.buildLocalPaymentOrderLogContext(memoryOrder)
598
+ });
599
+ }
500
600
  return memoryOrder;
601
+ }
602
+ if (options.operationId) {
603
+ this.logInfo("getLocalOrderByLookup-localPayment-开始读取SQLite", logContext);
604
+ }
501
605
  const orders = await this.loadOrdersFromSQLite();
502
606
  const localOrder = orders.find((order) => this.isOrderLookupMatch(order, key));
503
- if (!localOrder)
607
+ if (!localOrder) {
608
+ if (options.operationId) {
609
+ this.logWarning("getLocalOrderByLookup-localPayment-SQLite未命中", {
610
+ ...logContext,
611
+ sqlite_order_count: orders.length
612
+ });
613
+ }
614
+ return null;
615
+ }
616
+ this.store.list = this.normalizeOrdersForMemoryList(
617
+ orders,
618
+ "getLocalOrderByLookup.sqliteMemory"
619
+ );
620
+ this.syncOrdersMap();
621
+ const normalizedOrder = this.normalizeOrderForMemoryList(localOrder);
622
+ if (options.operationId) {
623
+ this.logInfo("getLocalOrderByLookup-localPayment-SQLite命中", {
624
+ ...logContext,
625
+ sqlite_order_count: orders.length,
626
+ ...this.buildLocalPaymentOrderLogContext(normalizedOrder)
627
+ });
628
+ }
629
+ return normalizedOrder;
630
+ }
631
+ async getLocalPayment(params) {
632
+ const logContext = {
633
+ operation_id: params.operationId,
634
+ sale_id: params.saleId,
635
+ payment_number: params.paymentNumber
636
+ };
637
+ this.logInfo("getLocalPayment-开始", logContext);
638
+ try {
639
+ const order = await this.getLocalOrderByLookup(params.saleId, {
640
+ operationId: params.operationId
641
+ });
642
+ if (!order) {
643
+ this.logWarning("getLocalPayment-订单未命中", logContext);
644
+ return null;
645
+ }
646
+ const payments = order.payments || [];
647
+ const match = (0, import_payment_utils.resolveOrderPaymentIdentity)(
648
+ payments,
649
+ params.paymentNumber,
650
+ "compat"
651
+ );
652
+ if (!match) {
653
+ this.logWarning("getLocalPayment-支付项未命中", {
654
+ ...logContext,
655
+ ...this.buildLocalPaymentOrderLogContext(order),
656
+ payments
657
+ });
658
+ return null;
659
+ }
660
+ this.logInfo("getLocalPayment-支付项命中", {
661
+ ...logContext,
662
+ ...this.buildLocalPaymentOrderLogContext(order),
663
+ matched_by: match.matchedBy,
664
+ payment_index: match.index,
665
+ payment: match.payment
666
+ });
667
+ return {
668
+ order,
669
+ payment: match.payment,
670
+ paymentIndex: match.index,
671
+ matchedBy: match.matchedBy
672
+ };
673
+ } catch (error) {
674
+ this.logError("getLocalPayment-查询失败", {
675
+ ...logContext,
676
+ error: error instanceof Error ? error.message : String(error)
677
+ });
678
+ throw error;
679
+ }
680
+ }
681
+ async updateLocalPayment(params) {
682
+ const logContext = {
683
+ operation_id: params.operationId,
684
+ sale_id: params.saleId,
685
+ payment_number: params.paymentNumber,
686
+ incoming_payment_update: params.updates
687
+ };
688
+ this.logInfo("updateLocalPayment-开始", logContext);
689
+ const found = await this.getLocalPayment(params);
690
+ if (!found) {
691
+ this.logWarning("updateLocalPayment-支付项未命中", logContext);
692
+ return null;
693
+ }
694
+ const previousPayment = (0, import_lodash_es.cloneDeep)(found.payment);
695
+ const nextPayment = (0, import_payment_utils.mergeOrderPaymentRecord)(found.payment, {
696
+ ...params.updates,
697
+ updated_at: params.updates.updated_at || (0, import_dayjs.default)().format("YYYY-MM-DD HH:mm:ss")
698
+ });
699
+ const nextOrder = {
700
+ ...found.order,
701
+ payments: (found.order.payments || []).map((payment, index) => index === found.paymentIndex ? nextPayment : payment),
702
+ updated_at: (0, import_dayjs.default)().format("YYYY-MM-DD HH:mm:ss")
703
+ };
704
+ const paymentContext = {
705
+ ...logContext,
706
+ matched_by: found.matchedBy,
707
+ payment_index: found.paymentIndex,
708
+ ...this.buildLocalPaymentOrderLogContext(nextOrder),
709
+ previous_payment: previousPayment,
710
+ payment: nextPayment
711
+ };
712
+ this.logInfo("updateLocalPayment-支付项合并完成", paymentContext);
713
+ const targetIndex = this.findOrderIndexByIdentity(this.store.list, found.order);
714
+ const shouldKeepInMemory = this.shouldKeepOrderInMemory(nextOrder);
715
+ if (targetIndex < 0 && shouldKeepInMemory) {
716
+ this.logError("updateLocalPayment-订单Store索引未命中", paymentContext);
504
717
  return null;
505
- this.store.list = orders.map((order) => this.normalizeOrderForMemoryList(order));
718
+ }
719
+ const previousList = this.store.list;
720
+ this.store.list = shouldKeepInMemory ? this.store.list.map((order, index) => index === targetIndex ? nextOrder : order) : this.store.list.filter((_, index) => index !== targetIndex);
506
721
  this.syncOrdersMap();
507
- return this.normalizeOrderForMemoryList(localOrder);
722
+ const storageOrder = this.withoutSyntheticDraftOrderId(nextOrder);
723
+ try {
724
+ this.logInfo("updateLocalPayment-SQLite写入开始", {
725
+ ...paymentContext,
726
+ storage_order_id: storageOrder.order_id ?? null
727
+ });
728
+ await this.updateOrderInSQLite(storageOrder, "updateLocalPayment", { throwOnError: true });
729
+ this.logInfo("updateLocalPayment-SQLite写入完成", {
730
+ ...paymentContext,
731
+ storage_order_id: storageOrder.order_id ?? null
732
+ });
733
+ } catch (error) {
734
+ this.store.list = previousList;
735
+ this.syncOrdersMap();
736
+ this.logError("updateLocalPayment-SQLite写入失败并回滚内存", {
737
+ ...paymentContext,
738
+ storage_order_id: storageOrder.order_id ?? null,
739
+ error: error instanceof Error ? error.message : String(error)
740
+ });
741
+ throw error;
742
+ }
743
+ if (shouldKeepInMemory || targetIndex >= 0) {
744
+ await this.emitOrdersChanged(
745
+ shouldKeepInMemory ? [nextOrder] : [],
746
+ shouldKeepInMemory ? "updateLocalPayment" : "updateLocalPayment.sqliteOnlyDraft"
747
+ );
748
+ }
749
+ this.logInfo("updateLocalPayment-变更事件完成", paymentContext);
750
+ return { order: nextOrder, payment: nextPayment, previousPayment };
508
751
  }
509
752
  async loadOrdersByServer() {
510
753
  var _a;
511
754
  let orderList = [];
755
+ const protectedOrdersBeforeRemoteFetch = (await this.loadOrdersFromSQLite()).filter(
756
+ (order) => this.shouldProtectLocalOrderFromRemoteSnapshot(order)
757
+ );
512
758
  this.logInfo("loadOrdersByServer-开始", {
513
759
  hasOrderDataSource: !!this.orderDataSource,
514
- query: ((_a = this.store) == null ? void 0 : _a.createdAtQuery) || null
760
+ query: ((_a = this.store) == null ? void 0 : _a.createdAtQuery) || null,
761
+ protectedLocalOrderCount: protectedOrdersBeforeRemoteFetch.length,
762
+ protectedPendingPaymentCount: protectedOrdersBeforeRemoteFetch.reduce(
763
+ (count, order) => count + this.getIdentifiedPendingPayments(order).length,
764
+ 0
765
+ )
515
766
  });
516
767
  if (this.orderDataSource) {
517
768
  try {
@@ -531,10 +782,19 @@ var OrderModule = class extends import_BaseModule.BaseModule {
531
782
  return this.store.list;
532
783
  }
533
784
  }
534
- const mergedOrders = await this.replaceOrdersSnapshotInSQLite(orderList, "loadOrdersByServer");
535
- const memoryOrders = mergedOrders.map((order) => this.normalizeOrderForMemoryList(order));
785
+ const mergedOrders = await this.replaceOrdersSnapshotInSQLite(
786
+ orderList,
787
+ "loadOrdersByServer",
788
+ protectedOrdersBeforeRemoteFetch
789
+ );
790
+ const memoryOrders = this.normalizeOrdersForMemoryList(
791
+ mergedOrders,
792
+ "loadOrdersByServer.memory"
793
+ );
536
794
  this.logInfo("loadOrdersByServer-结束", {
537
- count: memoryOrders.length
795
+ count: memoryOrders.length,
796
+ persistedCount: mergedOrders.length,
797
+ sqliteOnlyDraftCount: mergedOrders.length - memoryOrders.length
538
798
  });
539
799
  await this.core.effects.emit(import_types.OrderHooks.onOrdersLoaded, memoryOrders);
540
800
  return memoryOrders;
@@ -554,18 +814,14 @@ var OrderModule = class extends import_BaseModule.BaseModule {
554
814
  try {
555
815
  this.updateCreateStore();
556
816
  const orders = await this.loadOrdersByServer();
557
- if (orders.length > 0) {
558
- this.store.list = (0, import_lodash_es.cloneDeep)(orders);
559
- this.syncOrdersMap();
560
- this.emitOrdersChanged([], "silentRefresh");
561
- await this.core.effects.emit(import_types.OrderHooks.onOrdersSyncCompleted, null);
562
- this.logInfo("silentRefresh 完成", {
563
- orderCount: orders.length,
564
- duration: `${Date.now() - startTime}ms`
565
- });
566
- } else {
567
- this.logInfo("silentRefresh: 服务器未返回数据");
568
- }
817
+ this.store.list = (0, import_lodash_es.cloneDeep)(orders);
818
+ this.syncOrdersMap();
819
+ await this.emitOrdersChanged([], "silentRefresh");
820
+ await this.core.effects.emit(import_types.OrderHooks.onOrdersSyncCompleted, null);
821
+ this.logInfo("silentRefresh 完成", {
822
+ orderCount: orders.length,
823
+ duration: `${Date.now() - startTime}ms`
824
+ });
569
825
  return this.store.list;
570
826
  } catch (error) {
571
827
  const errorMessage = error instanceof Error ? error.message : String(error);
@@ -579,6 +835,9 @@ var OrderModule = class extends import_BaseModule.BaseModule {
579
835
  normalizeOrderForMemoryList(order, source = "normalizeOrderForMemoryList") {
580
836
  const normalizedOrder = this.normalizeOrderCollectionsForIngress(order, source);
581
837
  const externalSaleNumber = normalizedOrder == null ? void 0 : normalizedOrder.external_sale_number;
838
+ if (this.isDraftOrder(normalizedOrder) && !this.hasRealOrderId(normalizedOrder)) {
839
+ return normalizedOrder;
840
+ }
582
841
  if ((normalizedOrder == null ? void 0 : normalizedOrder.order_id) !== void 0 && (normalizedOrder == null ? void 0 : normalizedOrder.order_id) !== null && (normalizedOrder == null ? void 0 : normalizedOrder.order_id) !== "") {
583
842
  return normalizedOrder;
584
843
  }
@@ -590,6 +849,27 @@ var OrderModule = class extends import_BaseModule.BaseModule {
590
849
  order_id: externalSaleNumber
591
850
  };
592
851
  }
852
+ normalizeOrdersForMemoryList(orders, source) {
853
+ return orders.map((order) => this.normalizeOrderForMemoryList(order, source)).filter((order) => this.shouldKeepOrderInMemory(order));
854
+ }
855
+ hasRealOrderId(order) {
856
+ if (!order || this.isBlankIdentityValue(order.order_id))
857
+ return false;
858
+ const externalSaleNumber = order.external_sale_number;
859
+ return !(this.isDraftOrder(order) && !this.isBlankIdentityValue(externalSaleNumber) && String(order.order_id) === String(externalSaleNumber));
860
+ }
861
+ shouldKeepOrderInMemory(order) {
862
+ return !this.isDraftOrder(order) || this.hasRealOrderId(order);
863
+ }
864
+ withoutSyntheticDraftOrderId(order) {
865
+ const externalSaleNumber = order.external_sale_number;
866
+ if (Number(order.is_draft_order || 0) !== 1 || order.order_id === void 0 || externalSaleNumber === void 0 || String(order.order_id) !== String(externalSaleNumber)) {
867
+ return order;
868
+ }
869
+ const next = { ...order };
870
+ delete next.order_id;
871
+ return next;
872
+ }
593
873
  /**
594
874
  * 仅覆盖本地已存在的订单;不存在则直接跳过,不落库、不 emit。
595
875
  * 适用于"按 order_id 主动刷新本地订单详情"的代理场景。
@@ -638,13 +918,13 @@ var OrderModule = class extends import_BaseModule.BaseModule {
638
918
  * 将远端拉取的订单合并进本地 store(已存在则更新,不存在则追加),落库并 emit onOrdersChanged。
639
919
  * 供 /update/localOrder 等在「本地尚无该单」时写入新建单。
640
920
  */
641
- async upsertOrdersFromRemote(freshOrders) {
921
+ async upsertOrdersFromRemote(freshOrders, source = "upsertOrdersFromRemote") {
642
922
  if (!(freshOrders == null ? void 0 : freshOrders.length)) {
643
923
  this.logInfo("upsertOrdersFromRemote-订单列表为空", {});
644
924
  return;
645
925
  }
646
- this.logInfo("upsertOrdersFromRemote-开始", { count: freshOrders.length });
647
- await this.mergeOrdersToStore(freshOrders, "upsertOrdersFromRemote");
926
+ this.logInfo("upsertOrdersFromRemote-开始", { count: freshOrders.length, source });
927
+ await this.mergeOrdersToStore(freshOrders, source);
648
928
  }
649
929
  async markOrderSyncedByExternalSaleNumber(params) {
650
930
  const externalSaleNumber = params.externalSaleNumber;
@@ -670,7 +950,8 @@ var OrderModule = class extends import_BaseModule.BaseModule {
670
950
  ...existing,
671
951
  ...params.patch || {},
672
952
  external_sale_number: existing.external_sale_number ?? externalSaleNumber,
673
- need_sync: 0
953
+ need_sync: 0,
954
+ is_draft_order: 0
674
955
  });
675
956
  const storageKey = this.getOrderStorageKey(nextOrder) || key;
676
957
  this.store.list = this.store.list.map((order, index) => {
@@ -695,7 +976,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
695
976
  * 将本地待同步订单按 SQLite storage key 合并进 store 并落库。
696
977
  * checkout 离线兜底可能没有 order_id,但通常会有 external_sale_number。
697
978
  */
698
- async upsertPendingSyncOrders(pendingOrders) {
979
+ async upsertPendingSyncOrders(pendingOrders, options = {}) {
699
980
  if (!(pendingOrders == null ? void 0 : pendingOrders.length)) {
700
981
  this.logInfo("upsertPendingSyncOrders-订单列表为空", {});
701
982
  return;
@@ -707,14 +988,19 @@ var OrderModule = class extends import_BaseModule.BaseModule {
707
988
  order,
708
989
  "upsertPendingSyncOrders.incoming"
709
990
  );
710
- const storageKey = this.getOrderStorageKey(normalizedOrder);
991
+ const pendingOrder = {
992
+ ...normalizedOrder,
993
+ need_sync: 1,
994
+ is_draft_order: 0
995
+ };
996
+ const storageKey = this.getOrderStorageKey(pendingOrder);
711
997
  if (!storageKey)
712
998
  continue;
713
- pendingMap.set(storageKey, normalizedOrder);
999
+ pendingMap.set(storageKey, pendingOrder);
714
1000
  memoryPendingMap.set(
715
1001
  storageKey,
716
1002
  this.normalizeOrderForMemoryList(
717
- normalizedOrder,
1003
+ pendingOrder,
718
1004
  "upsertPendingSyncOrders.memory"
719
1005
  )
720
1006
  );
@@ -745,7 +1031,12 @@ var OrderModule = class extends import_BaseModule.BaseModule {
745
1031
  if (matchIndex >= 0) {
746
1032
  const existingOrder = updatedList[matchIndex];
747
1033
  const mergedOrder = this.mergeOrderRecords(existingOrder, pendingOrder);
748
- updatedList[matchIndex] = mergedOrder;
1034
+ const nextOrder = {
1035
+ ...mergedOrder,
1036
+ need_sync: 1,
1037
+ is_draft_order: 0
1038
+ };
1039
+ updatedList[matchIndex] = nextOrder;
749
1040
  const pendingSnapshot = pendingMap.get(storageKey);
750
1041
  if (pendingSnapshot) {
751
1042
  pendingMap.set(
@@ -753,7 +1044,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
753
1044
  this.reconcileOverwriteOrderCollections(existingOrder, pendingSnapshot)
754
1045
  );
755
1046
  }
756
- this.registerOrderIdentityKeys(identityKeyToIndex, mergedOrder, matchIndex);
1047
+ this.registerOrderIdentityKeys(identityKeyToIndex, nextOrder, matchIndex);
757
1048
  mergeActions[storageKey] = "update";
758
1049
  continue;
759
1050
  }
@@ -763,15 +1054,131 @@ var OrderModule = class extends import_BaseModule.BaseModule {
763
1054
  mergeActions[storageKey] = "insert";
764
1055
  }
765
1056
  const patchedOrders = [...pendingMap.values()];
1057
+ const previousList = this.store.list;
766
1058
  this.store.list = this.runOrderStoreSelfCheck(updatedList, "upsertPendingSyncOrders.store");
767
1059
  this.syncOrdersMap();
768
- await this.patchOrdersInSQLite(patchedOrders, "upsertPendingSyncOrders", mergeActions);
1060
+ try {
1061
+ await this.patchOrdersInSQLite(
1062
+ patchedOrders,
1063
+ "upsertPendingSyncOrders",
1064
+ mergeActions,
1065
+ options
1066
+ );
1067
+ } catch (error) {
1068
+ this.store.list = previousList;
1069
+ this.syncOrdersMap();
1070
+ throw error;
1071
+ }
769
1072
  this.logInfo("upsertPendingSyncOrders-结束", {
770
1073
  count: patchedOrders.length,
771
1074
  storeOrderCountAfter: this.store.list.length
772
1075
  });
773
1076
  await this.emitOrdersChanged(patchedOrders, "upsertPendingSyncOrders");
774
1077
  }
1078
+ /**
1079
+ * 将 POS 草稿订单按 SQLite storage key 落库。
1080
+ * 只有具备真实 order_id 的草稿进入 store;其余草稿仅供本地恢复查询。
1081
+ */
1082
+ async upsertLocalDraftOrders(draftOrders) {
1083
+ if (!(draftOrders == null ? void 0 : draftOrders.length)) {
1084
+ this.logInfo("upsertLocalDraftOrders-订单列表为空", {});
1085
+ return;
1086
+ }
1087
+ const draftMap = /* @__PURE__ */ new Map();
1088
+ const memoryDraftMap = /* @__PURE__ */ new Map();
1089
+ for (const order of draftOrders) {
1090
+ const draftOrder = {
1091
+ ...order,
1092
+ need_sync: 0,
1093
+ is_draft_order: 1
1094
+ };
1095
+ const storageKey = this.getOrderStorageKey(draftOrder);
1096
+ if (!storageKey)
1097
+ continue;
1098
+ draftMap.set(storageKey, draftOrder);
1099
+ const memoryDraftOrder = this.normalizeOrderForMemoryList(draftOrder);
1100
+ if (this.shouldKeepOrderInMemory(memoryDraftOrder)) {
1101
+ memoryDraftMap.set(storageKey, memoryDraftOrder);
1102
+ }
1103
+ }
1104
+ if (draftMap.size === 0) {
1105
+ this.logError("upsertLocalDraftOrders-订单缺少可落库标识", {
1106
+ count: draftOrders.length
1107
+ });
1108
+ return;
1109
+ }
1110
+ this.logInfo("upsertLocalDraftOrders-开始", {
1111
+ count: draftMap.size,
1112
+ memoryDraftCount: memoryDraftMap.size,
1113
+ sqliteOnlyDraftCount: draftMap.size - memoryDraftMap.size
1114
+ });
1115
+ const mergeActions = {};
1116
+ const updatedList = this.store.list.filter((order) => this.shouldKeepOrderInMemory(order));
1117
+ const identityKeyToIndex = /* @__PURE__ */ new Map();
1118
+ for (let index = 0; index < updatedList.length; index += 1) {
1119
+ for (const key of this.getOrderIdentityMatchKeys(updatedList[index])) {
1120
+ if (!identityKeyToIndex.has(key))
1121
+ identityKeyToIndex.set(key, index);
1122
+ }
1123
+ }
1124
+ for (const [storageKey, draftOrder] of memoryDraftMap.entries()) {
1125
+ const matchIndex = this.lookupOrderIndexByIdentityKeys(
1126
+ identityKeyToIndex,
1127
+ this.getOrderIdentityMatchKeys(draftOrder)
1128
+ );
1129
+ if (matchIndex >= 0) {
1130
+ const existingOrder = updatedList[matchIndex];
1131
+ const reconciledDraftOrder = this.preserveExistingBusinessCodeForDraft(
1132
+ existingOrder,
1133
+ draftOrder
1134
+ );
1135
+ const mergedOrder = this.mergeOrderRecords(existingOrder, reconciledDraftOrder);
1136
+ const nextOrder = {
1137
+ ...mergedOrder,
1138
+ need_sync: 0,
1139
+ is_draft_order: 1
1140
+ };
1141
+ updatedList[matchIndex] = nextOrder;
1142
+ draftMap.set(storageKey, reconciledDraftOrder);
1143
+ this.registerOrderIdentityKeys(identityKeyToIndex, nextOrder, matchIndex);
1144
+ mergeActions[storageKey] = "update";
1145
+ continue;
1146
+ }
1147
+ const insertIndex = updatedList.length;
1148
+ updatedList.push(draftOrder);
1149
+ this.registerOrderIdentityKeys(identityKeyToIndex, draftOrder, insertIndex);
1150
+ mergeActions[storageKey] = "insert";
1151
+ }
1152
+ const patchedOrders = [...draftMap.values()];
1153
+ const previousList = this.store.list;
1154
+ this.store.list = this.runOrderStoreSelfCheck(updatedList, "upsertLocalDraftOrders.store");
1155
+ this.syncOrdersMap();
1156
+ try {
1157
+ await this.patchOrdersInSQLite(
1158
+ patchedOrders,
1159
+ "upsertLocalDraftOrders",
1160
+ mergeActions,
1161
+ { throwOnError: true }
1162
+ );
1163
+ } catch (error) {
1164
+ this.store.list = previousList;
1165
+ this.syncOrdersMap();
1166
+ throw error;
1167
+ }
1168
+ this.logInfo("upsertLocalDraftOrders-结束", {
1169
+ count: patchedOrders.length,
1170
+ storeOrderCountAfter: this.store.list.length
1171
+ });
1172
+ const removedLegacyMemoryDraft = previousList.some(
1173
+ (order) => !this.shouldKeepOrderInMemory(order)
1174
+ );
1175
+ if (memoryDraftMap.size > 0 || removedLegacyMemoryDraft) {
1176
+ await this.emitOrdersChanged(
1177
+ patchedOrders.filter((order) => this.shouldKeepOrderInMemory(order)),
1178
+ "upsertLocalDraftOrders"
1179
+ );
1180
+ }
1181
+ }
775
1182
  /**
776
1183
  * 通过 SSE 按自定义 query 拉取订单(支持 select/with 精简字段)
777
1184
  */
@@ -804,6 +1211,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
804
1211
  super.destroy();
805
1212
  }
806
1213
  syncOrdersMap() {
1214
+ this.store.list = this.store.list.filter((order) => this.shouldKeepOrderInMemory(order));
807
1215
  this.store.map.clear();
808
1216
  for (const order of this.store.list) {
809
1217
  const primaryIdentity = this.getOrderMapKey(order);
@@ -827,6 +1235,22 @@ var OrderModule = class extends import_BaseModule.BaseModule {
827
1235
  isBlankIdentityValue(value) {
828
1236
  return value === void 0 || value === null || value === "";
829
1237
  }
1238
+ /**
1239
+ * `/sales/draft` 的旧调用方可能把未设置的 business_code 传成空串。
1240
+ * 对同一真实订单,空值不能覆盖已有的分类,否则 booking 查询会把该订单从 Party 列表过滤掉。
1241
+ */
1242
+ preserveExistingBusinessCodeForDraft(existingOrder, draftOrder) {
1243
+ const existingBusinessCode = existingOrder.business_code;
1244
+ const draftBusinessCode = draftOrder.business_code;
1245
+ const hasExistingBusinessCode = typeof existingBusinessCode === "string" && existingBusinessCode.trim() !== "";
1246
+ const hasDraftBusinessCode = typeof draftBusinessCode === "string" && draftBusinessCode.trim() !== "";
1247
+ if (!hasExistingBusinessCode || hasDraftBusinessCode)
1248
+ return draftOrder;
1249
+ return {
1250
+ ...draftOrder,
1251
+ business_code: existingBusinessCode
1252
+ };
1253
+ }
830
1254
  getOrderIdentityEntries(order) {
831
1255
  if (!order)
832
1256
  return [];
@@ -1082,10 +1506,20 @@ var OrderModule = class extends import_BaseModule.BaseModule {
1082
1506
  });
1083
1507
  this.logDuplicateOrders(`${source}.beforeMerge`, this.store.list);
1084
1508
  const normalizedFreshOrders = [];
1509
+ const incomingProductsHaveStableIdentity = /* @__PURE__ */ new Map();
1085
1510
  for (const order of freshOrders) {
1086
1511
  const id = order == null ? void 0 : order.order_id;
1087
1512
  if (id === void 0 || id === null)
1088
1513
  continue;
1514
+ const rawProducts = order.products;
1515
+ if (Array.isArray(rawProducts)) {
1516
+ const allProductsHaveStableIdentity = this.doProductsHaveStableMembershipIdentity(rawProducts);
1517
+ const orderIdKey = this.getIdKey(id);
1518
+ incomingProductsHaveStableIdentity.set(
1519
+ orderIdKey,
1520
+ (incomingProductsHaveStableIdentity.get(orderIdKey) ?? true) && allProductsHaveStableIdentity
1521
+ );
1522
+ }
1089
1523
  normalizedFreshOrders.push(this.normalizeRemoteSyncedOrder(order));
1090
1524
  }
1091
1525
  const uniqueFreshOrders = this.compactOrderListByIdentity(
@@ -1094,7 +1528,14 @@ var OrderModule = class extends import_BaseModule.BaseModule {
1094
1528
  ).list;
1095
1529
  const uniqueFreshCount = uniqueFreshOrders.length;
1096
1530
  const patchedOrders = [];
1531
+ const remoteProductMembershipChanges = [];
1532
+ const forceWriteStorageKeys = /* @__PURE__ */ new Set();
1097
1533
  const mergeActions = {};
1534
+ const existingProductsHaveStableIdentity = this.store.list.map(
1535
+ (order) => this.doProductsHaveStableMembershipIdentity(
1536
+ order.products
1537
+ )
1538
+ );
1098
1539
  const updatedList = this.store.list.map(
1099
1540
  (order) => this.normalizeOrderForMemoryList(order, `${source}.existingMemory`)
1100
1541
  );
@@ -1126,9 +1567,63 @@ var OrderModule = class extends import_BaseModule.BaseModule {
1126
1567
  });
1127
1568
  continue;
1128
1569
  }
1129
- const mergedOrder = this.mergeOrderRecords(existingOrder, fresh);
1130
- updatedList[matchIndex] = mergedOrder;
1131
- this.registerOrderIdentityKeys(identityKeyToIndex, mergedOrder, matchIndex);
1570
+ if (this.isDraftOrder(existingOrder) && this.shouldProtectDraftOrderFromIncoming(source)) {
1571
+ this.logInfo("mergeOrdersToStore-跳过 draft 订单远端覆盖", {
1572
+ source,
1573
+ order_id: existingOrder.order_id ?? fresh.order_id ?? null,
1574
+ external_sale_number: existingOrder.external_sale_number ?? fresh.external_sale_number ?? null,
1575
+ local_is_draft_order: existingOrder.is_draft_order ?? null,
1576
+ remote_is_draft_order: fresh.is_draft_order ?? null,
1577
+ reason: "protect_draft_order_from_remote_echo"
1578
+ });
1579
+ continue;
1580
+ }
1581
+ let mergedOrder = this.mergeOrderRecords(existingOrder, fresh);
1582
+ const freshOrderId = fresh.order_id;
1583
+ const incomingProductsAreStable = freshOrderId !== void 0 && freshOrderId !== null && incomingProductsHaveStableIdentity.get(this.getIdKey(freshOrderId)) !== false;
1584
+ let membershipChange = null;
1585
+ if (Array.isArray(fresh.products) && freshOrderId !== void 0 && freshOrderId !== null && existingProductsHaveStableIdentity[matchIndex] !== false && incomingProductsAreStable) {
1586
+ membershipChange = this.getRemoteProductMembershipChange(
1587
+ existingOrder,
1588
+ mergedOrder,
1589
+ source
1590
+ );
1591
+ }
1592
+ let mergedProductsHaveStableIdentity = existingProductsHaveStableIdentity[matchIndex] !== false && incomingProductsAreStable;
1593
+ let adjustedMatchIndex = matchIndex;
1594
+ const duplicateIndexes = /* @__PURE__ */ new Set();
1595
+ for (const key of freshIdentityKeys) {
1596
+ const duplicateIndex = identityKeyToIndex.get(key);
1597
+ if (duplicateIndex === void 0 || duplicateIndex === matchIndex)
1598
+ continue;
1599
+ duplicateIndexes.add(duplicateIndex);
1600
+ }
1601
+ if (duplicateIndexes.size > 0) {
1602
+ for (const duplicateIndex of [...duplicateIndexes].sort((left, right) => right - left)) {
1603
+ mergedOrder = this.mergeOrderRecords(mergedOrder, updatedList[duplicateIndex]);
1604
+ mergedProductsHaveStableIdentity = mergedProductsHaveStableIdentity && existingProductsHaveStableIdentity[duplicateIndex] !== false;
1605
+ updatedList.splice(duplicateIndex, 1);
1606
+ existingProductsHaveStableIdentity.splice(duplicateIndex, 1);
1607
+ if (duplicateIndex < adjustedMatchIndex)
1608
+ adjustedMatchIndex -= 1;
1609
+ }
1610
+ identityKeyToIndex.clear();
1611
+ for (let index = 0; index < updatedList.length; index += 1) {
1612
+ for (const key of this.getOrderIdentityMatchKeys(updatedList[index])) {
1613
+ if (!identityKeyToIndex.has(key))
1614
+ identityKeyToIndex.set(key, index);
1615
+ }
1616
+ }
1617
+ }
1618
+ updatedList[adjustedMatchIndex] = mergedOrder;
1619
+ existingProductsHaveStableIdentity[adjustedMatchIndex] = mergedProductsHaveStableIdentity;
1620
+ if (membershipChange) {
1621
+ remoteProductMembershipChanges.push(membershipChange);
1622
+ const mergedStorageKey = this.getOrderStorageKey(mergedOrder);
1623
+ if (mergedStorageKey)
1624
+ forceWriteStorageKeys.add(mergedStorageKey);
1625
+ }
1626
+ this.registerOrderIdentityKeys(identityKeyToIndex, mergedOrder, adjustedMatchIndex);
1132
1627
  patchedOrders.push(mergedOrder);
1133
1628
  if (mergeKey)
1134
1629
  mergeActions[mergeKey] = "update";
@@ -1136,6 +1631,9 @@ var OrderModule = class extends import_BaseModule.BaseModule {
1136
1631
  }
1137
1632
  const insertIndex = updatedList.length;
1138
1633
  updatedList.push(fresh);
1634
+ existingProductsHaveStableIdentity.push(
1635
+ fresh.order_id !== void 0 && fresh.order_id !== null && incomingProductsHaveStableIdentity.get(this.getIdKey(fresh.order_id)) !== false
1636
+ );
1139
1637
  this.registerOrderIdentityKeys(identityKeyToIndex, fresh, insertIndex);
1140
1638
  patchedOrders.push(fresh);
1141
1639
  if (mergeKey)
@@ -1150,13 +1648,20 @@ var OrderModule = class extends import_BaseModule.BaseModule {
1150
1648
  });
1151
1649
  this.store.list = this.runOrderStoreSelfCheck(updatedList, `${source}.store`);
1152
1650
  this.syncOrdersMap();
1153
- await this.patchOrdersInSQLite(patchedOrders, source, mergeActions);
1651
+ await this.patchOrdersInSQLite(
1652
+ patchedOrders,
1653
+ source,
1654
+ mergeActions,
1655
+ {},
1656
+ forceWriteStorageKeys
1657
+ );
1154
1658
  this.logInfo("mergeOrdersToStore-结束", {
1155
1659
  uniqueFreshCount,
1156
1660
  storeOrderCountAfter: this.store.list.length,
1157
1661
  insertCount,
1158
1662
  updateCount
1159
1663
  });
1664
+ await this.emitRemoteProductMembershipChanges(remoteProductMembershipChanges);
1160
1665
  await this.emitOrdersChanged(patchedOrders, source);
1161
1666
  }
1162
1667
  /**
@@ -1180,6 +1685,72 @@ var OrderModule = class extends import_BaseModule.BaseModule {
1180
1685
  isRemoteEchoMergeSource(source) {
1181
1686
  return source === "pubsub.bodyUpsert" || source === "pubsub.httpRefresh";
1182
1687
  }
1688
+ getProductMembershipKey(product) {
1689
+ const persistentId = (0, import_orderCollectionIdentity.getOrderCollectionPersistentId)("product", product);
1690
+ if (persistentId)
1691
+ return `order_detail_id:${persistentId}`;
1692
+ const uid = (0, import_orderCollectionIdentity.getOrderCollectionUid)("product", product);
1693
+ return uid ? `unique_identification_number:${uid}` : "";
1694
+ }
1695
+ doProductsHaveStableMembershipIdentity(products) {
1696
+ if (!Array.isArray(products))
1697
+ return true;
1698
+ return products.every(
1699
+ (product) => Boolean(
1700
+ (0, import_orderCollectionIdentity.getOrderCollectionPersistentId)("product", product) || (0, import_orderCollectionIdentity.getOrderCollectionUid)("product", product)
1701
+ )
1702
+ );
1703
+ }
1704
+ /**
1705
+ * 冲突提示关注逻辑商品行,而不是服务端 checkout 过程中可能被重建的 detail 记录。
1706
+ * 通用集合合并仍以 order_detail_id 为事实主键;这里只允许相同稳定 UID 跨 detail id 对齐,
1707
+ * 避免本机 checkout 回声被误判成“一删一增”。
1708
+ */
1709
+ isSameProductMembershipItem(left, right) {
1710
+ if ((0, import_orderCollectionIdentity.isSameOrderCollectionItem)("product", left, right))
1711
+ return true;
1712
+ const leftUid = (0, import_orderCollectionIdentity.getOrderCollectionUid)("product", left);
1713
+ const rightUid = (0, import_orderCollectionIdentity.getOrderCollectionUid)("product", right);
1714
+ return Boolean(leftUid && rightUid && leftUid === rightUid);
1715
+ }
1716
+ /**
1717
+ * 对比合并前后的商品成员集合。商品字段修改与数组重排不会产生 diff。
1718
+ */
1719
+ getRemoteProductMembershipChange(existingOrder, mergedOrder, source) {
1720
+ if (!this.isRemoteEchoMergeSource(source))
1721
+ return null;
1722
+ const previousProducts = Array.isArray(existingOrder.products) ? existingOrder.products : [];
1723
+ const currentProducts = Array.isArray(mergedOrder.products) ? mergedOrder.products : [];
1724
+ const unmatchedCurrentIndexes = new Set(currentProducts.map((_, index) => index));
1725
+ const removedProductKeys = [];
1726
+ for (const previousProduct of previousProducts) {
1727
+ const matchedIndex = currentProducts.findIndex(
1728
+ (currentProduct, index) => unmatchedCurrentIndexes.has(index) && this.isSameProductMembershipItem(previousProduct, currentProduct)
1729
+ );
1730
+ if (matchedIndex >= 0) {
1731
+ unmatchedCurrentIndexes.delete(matchedIndex);
1732
+ continue;
1733
+ }
1734
+ const productKey = this.getProductMembershipKey(previousProduct);
1735
+ if (productKey)
1736
+ removedProductKeys.push(productKey);
1737
+ }
1738
+ const addedProductKeys = [...unmatchedCurrentIndexes].map((index) => this.getProductMembershipKey(currentProducts[index])).filter(Boolean);
1739
+ if (addedProductKeys.length === 0 && removedProductKeys.length === 0) {
1740
+ return null;
1741
+ }
1742
+ const orderId = mergedOrder.order_id ?? existingOrder.order_id;
1743
+ if (orderId === void 0 || orderId === null)
1744
+ return null;
1745
+ return {
1746
+ order_id: orderId,
1747
+ source,
1748
+ added_product_keys: addedProductKeys,
1749
+ removed_product_keys: removedProductKeys,
1750
+ previous_product_count: previousProducts.length,
1751
+ current_product_count: currentProducts.length
1752
+ };
1753
+ }
1183
1754
  /**
1184
1755
  * 将订单的全部身份键登记到索引并指向其在列表中的下标。
1185
1756
  * 合并后订单新增的身份字段(如挂单同步后拿到 order_id)也会被登记。
@@ -1320,6 +1891,72 @@ var OrderModule = class extends import_BaseModule.BaseModule {
1320
1891
  return right;
1321
1892
  return left;
1322
1893
  }
1894
+ getProductIdentityKeys(product) {
1895
+ var _a;
1896
+ const record = product;
1897
+ if (!record)
1898
+ return [];
1899
+ const keys = [];
1900
+ const metadataUid = (_a = record.metadata) == null ? void 0 : _a.unique_identification_number;
1901
+ if (!this.isBlankIdentityValue(metadataUid))
1902
+ keys.push(`uid:${String(metadataUid)}`);
1903
+ const topLevelUid = record.unique_identification_number;
1904
+ if (!this.isBlankIdentityValue(topLevelUid))
1905
+ keys.push(`uid:${String(topLevelUid)}`);
1906
+ const orderDetailId = record.order_detail_id;
1907
+ if (!this.isBlankIdentityValue(orderDetailId))
1908
+ keys.push(`detail:${String(orderDetailId)}`);
1909
+ const bookingUid = record.booking_uid;
1910
+ if (!this.isBlankIdentityValue(bookingUid))
1911
+ keys.push(`booking:${String(bookingUid)}`);
1912
+ return keys;
1913
+ }
1914
+ mergeOrderProductLists(preferred, secondary) {
1915
+ const preferredList = Array.isArray(preferred) ? preferred : [];
1916
+ const secondaryList = Array.isArray(secondary) ? secondary : [];
1917
+ if (secondaryList.length === 0)
1918
+ return preferredList;
1919
+ if (preferredList.length === 0)
1920
+ return secondaryList;
1921
+ const seenKeys = /* @__PURE__ */ new Set();
1922
+ for (const item of preferredList) {
1923
+ const keys = this.getProductIdentityKeys(item);
1924
+ for (const key of keys)
1925
+ seenKeys.add(key);
1926
+ }
1927
+ const result = [...preferredList];
1928
+ for (const item of secondaryList) {
1929
+ const keys = this.getProductIdentityKeys(item);
1930
+ if (keys.length === 0 || keys.some((key) => seenKeys.has(key)))
1931
+ continue;
1932
+ for (const key of keys)
1933
+ seenKeys.add(key);
1934
+ result.push((0, import_lodash_es.cloneDeep)(item));
1935
+ }
1936
+ return result;
1937
+ }
1938
+ mergeOrderPaymentLists(existing, incoming) {
1939
+ const existingList = Array.isArray(existing) ? existing : [];
1940
+ const incomingList = Array.isArray(incoming) ? incoming : [];
1941
+ if (incomingList.length === 0)
1942
+ return (0, import_lodash_es.cloneDeep)(existingList);
1943
+ return (0, import_payment_utils.mergeOrderPaymentListsByIdentity)(existingList, incomingList);
1944
+ }
1945
+ getIdentifiedPendingPayments(order) {
1946
+ const payments = Array.isArray(order == null ? void 0 : order.payments) ? order.payments : [];
1947
+ return payments.filter((payment) => (0, import_payment_utils.isIdentifiedPendingOrderPayment)(payment));
1948
+ }
1949
+ shouldProtectLocalOrderFromRemoteSnapshot(order) {
1950
+ return this.isPendingSyncOrder(order) || this.isDraftOrder(order) || this.getIdentifiedPendingPayments(order).length > 0;
1951
+ }
1952
+ getUnmatchedIdentifiedPendingPayments(existing, incoming) {
1953
+ const existingPayments = Array.isArray(existing) ? existing : [];
1954
+ const incomingPayments = Array.isArray(incoming) ? incoming : [];
1955
+ const incomingIdentityKeys = new Set(
1956
+ incomingPayments.flatMap((payment) => (0, import_payment_utils.getOrderPaymentIdentityKeys)(payment))
1957
+ );
1958
+ return existingPayments.filter((payment) => (0, import_payment_utils.isIdentifiedPendingOrderPayment)(payment) && !(0, import_payment_utils.getOrderPaymentIdentityKeys)(payment).some((key) => incomingIdentityKeys.has(key)));
1959
+ }
1323
1960
  mergeOrderRecords(existing, incoming) {
1324
1961
  const preferred = this.pickPreferredOrder(existing, incoming);
1325
1962
  const secondary = preferred === existing ? incoming : existing;
@@ -1349,7 +1986,8 @@ var OrderModule = class extends import_BaseModule.BaseModule {
1349
1986
  "contacts_info",
1350
1987
  "phone",
1351
1988
  "email",
1352
- "relation_forms"
1989
+ "relation_forms",
1990
+ "is_draft_order"
1353
1991
  ];
1354
1992
  const mergedRecord = merged;
1355
1993
  const preferredRecord = preferred;
@@ -1377,11 +2015,26 @@ var OrderModule = class extends import_BaseModule.BaseModule {
1377
2015
  }
1378
2016
  for (const field of ORDER_AUTHORITATIVE_COLLECTION_FIELDS) {
1379
2017
  if (Array.isArray(incomingRecord[field])) {
2018
+ if (field === "payments") {
2019
+ const preservedPending = this.getUnmatchedIdentifiedPendingPayments(
2020
+ existingRecord[field],
2021
+ incomingRecord[field]
2022
+ );
2023
+ if (preservedPending.length > 0) {
2024
+ this.logInfo("mergeOrderRecords-保留本地 pending 支付项", {
2025
+ order_id: existing.order_id ?? incoming.order_id ?? null,
2026
+ external_sale_number: existingRecord.external_sale_number ?? incomingRecord.external_sale_number ?? null,
2027
+ preserved_pending_count: preservedPending.length,
2028
+ preserved_payment_identities: preservedPending.map((payment) => (0, import_payment_utils.getOrderPaymentIdentityKeys)(payment))
2029
+ });
2030
+ }
2031
+ }
1380
2032
  mergedRecord[field] = this.mergeAuthoritativeOrderCollection(
1381
2033
  field,
1382
2034
  existingRecord[field],
1383
2035
  incomingRecord[field],
1384
- childUidRemaps
2036
+ childUidRemaps,
2037
+ { preserveIdentifiedPendingPayments: field === "payments" }
1385
2038
  );
1386
2039
  continue;
1387
2040
  }
@@ -1416,7 +2069,8 @@ var OrderModule = class extends import_BaseModule.BaseModule {
1416
2069
  external_sale_number: order.external_sale_number ?? null,
1417
2070
  shop_order_number: order.shop_order_number ?? null,
1418
2071
  shop_full_order_number: order.shop_full_order_number ?? null,
1419
- need_sync: order.need_sync ?? null
2072
+ need_sync: order.need_sync ?? null,
2073
+ is_draft_order: order.is_draft_order ?? null
1420
2074
  }))
1421
2075
  }));
1422
2076
  }
@@ -1533,18 +2187,48 @@ var OrderModule = class extends import_BaseModule.BaseModule {
1533
2187
  isPendingSyncOrder(order) {
1534
2188
  return Number(order == null ? void 0 : order.need_sync) === 1;
1535
2189
  }
2190
+ isDraftOrder(order) {
2191
+ return Number(order == null ? void 0 : order.is_draft_order) === 1;
2192
+ }
2193
+ shouldProtectDraftOrderFromIncoming(source) {
2194
+ return source !== "checkoutSync";
2195
+ }
1536
2196
  normalizeRemoteSyncedOrder(order) {
1537
2197
  const normalizedOrder = this.normalizeOrderCollectionsForIngress(
1538
2198
  order,
1539
2199
  "normalizeRemoteSyncedOrder"
1540
2200
  );
1541
2201
  if (this.isPendingSyncOrder(normalizedOrder))
1542
- return normalizedOrder;
2202
+ return {
2203
+ ...normalizedOrder,
2204
+ is_draft_order: 0
2205
+ };
1543
2206
  return {
1544
2207
  ...normalizedOrder,
1545
- need_sync: 0
2208
+ need_sync: 0,
2209
+ is_draft_order: 0
1546
2210
  };
1547
2211
  }
2212
+ mergeRemoteIdentityIntoDraftOrder(draftOrder, remoteOrder) {
2213
+ const merged = {
2214
+ ...this.withoutSyntheticDraftOrderId(draftOrder)
2215
+ };
2216
+ const remote = remoteOrder;
2217
+ for (const field of [
2218
+ "order_id",
2219
+ "order_number",
2220
+ "shop_order_number",
2221
+ "shop_full_order_number"
2222
+ ]) {
2223
+ if (this.isBlankIdentityValue(merged[field]) && !this.isBlankIdentityValue(remote[field])) {
2224
+ merged[field] = remote[field];
2225
+ }
2226
+ }
2227
+ return this.normalizeOrderCollectionsForIngress(
2228
+ merged,
2229
+ "mergeRemoteIdentityIntoDraftOrder"
2230
+ );
2231
+ }
1548
2232
  mergeRemoteSnapshotWithPendingOrders(remoteOrders, existingOrders) {
1549
2233
  const merged = remoteOrders.map((order) => this.normalizeRemoteSyncedOrder(order));
1550
2234
  for (const rawOrder of existingOrders) {
@@ -1552,11 +2236,18 @@ var OrderModule = class extends import_BaseModule.BaseModule {
1552
2236
  rawOrder,
1553
2237
  "mergeRemoteSnapshotWithPendingOrders.existingSQLite"
1554
2238
  );
1555
- if (!this.isPendingSyncOrder(order))
2239
+ if (!this.shouldProtectLocalOrderFromRemoteSnapshot(order))
1556
2240
  continue;
1557
2241
  const matchIndex = this.findOrderIndexByIdentity(merged, order);
1558
2242
  if (matchIndex >= 0) {
1559
- merged[matchIndex] = this.mergeOrderRecords(order, merged[matchIndex]);
2243
+ if (this.isDraftOrder(order)) {
2244
+ merged[matchIndex] = this.mergeRemoteIdentityIntoDraftOrder(
2245
+ order,
2246
+ merged[matchIndex]
2247
+ );
2248
+ } else {
2249
+ merged[matchIndex] = this.mergeOrderRecords(order, merged[matchIndex]);
2250
+ }
1560
2251
  continue;
1561
2252
  }
1562
2253
  if (!this.getOrderStorageKey(order))
@@ -1584,8 +2275,13 @@ var OrderModule = class extends import_BaseModule.BaseModule {
1584
2275
  * @example
1585
2276
  * await this.patchOrdersInSQLite(freshOrders)
1586
2277
  */
1587
- async patchOrdersInSQLite(orders, source, mergeActions = {}) {
1588
- if (!this.dbManager || orders.length === 0) {
2278
+ async patchOrdersInSQLite(orders, source, mergeActions = {}, options = {}, forceWriteStorageKeys = /* @__PURE__ */ new Set()) {
2279
+ if (orders.length === 0) {
2280
+ return;
2281
+ }
2282
+ if (!this.dbManager) {
2283
+ if (options.throwOnError)
2284
+ throw new Error("订单 SQLite 数据库未初始化");
1589
2285
  return;
1590
2286
  }
1591
2287
  const ordersSnapshot = (0, import_lodash_es.cloneDeep)(orders).map((order) => this.normalizeOrderCollectionsForIngress(order, `${source}.sqliteIngress`)).filter((order) => this.getOrderStorageKey(order));
@@ -1595,7 +2291,8 @@ var OrderModule = class extends import_BaseModule.BaseModule {
1595
2291
  const { toWrite, skipped } = this.filterRedundantPatchOrders(
1596
2292
  source,
1597
2293
  ordersSnapshot,
1598
- mergeActions
2294
+ mergeActions,
2295
+ forceWriteStorageKeys
1599
2296
  );
1600
2297
  if (toWrite.length === 0) {
1601
2298
  return;
@@ -1623,6 +2320,8 @@ var OrderModule = class extends import_BaseModule.BaseModule {
1623
2320
  for (const order of compactedToWrite) {
1624
2321
  await this.dbManager.update(INDEXDB_STORE_NAME, order);
1625
2322
  }
2323
+ } else if (options.throwOnError) {
2324
+ throw new Error("订单 SQLite 数据库不支持写入");
1626
2325
  }
1627
2326
  this.recordRecentSqliteWrite(source, compactedToWrite);
1628
2327
  this.logInfo("patchOrdersInSQLite-完成", {
@@ -1637,6 +2336,8 @@ var OrderModule = class extends import_BaseModule.BaseModule {
1637
2336
  error: error instanceof Error ? error.message : String(error),
1638
2337
  orderCount: ordersSnapshot.length
1639
2338
  });
2339
+ if (options.throwOnError)
2340
+ throw error;
1640
2341
  }
1641
2342
  }
1642
2343
  /**
@@ -1645,8 +2346,15 @@ var OrderModule = class extends import_BaseModule.BaseModule {
1645
2346
  * @example
1646
2347
  * await this.updateOrderInSQLite(freshOrder)
1647
2348
  */
1648
- async updateOrderInSQLite(order, source) {
1649
- if (!this.dbManager || !this.getOrderStorageKey(order)) {
2349
+ async updateOrderInSQLite(order, source, options = {}) {
2350
+ if (!this.getOrderStorageKey(order)) {
2351
+ if (options.throwOnError)
2352
+ throw new Error("订单缺少可用的 SQLite 存储主键");
2353
+ return;
2354
+ }
2355
+ if (!this.dbManager) {
2356
+ if (options.throwOnError)
2357
+ throw new Error("订单 SQLite 数据库未初始化");
1650
2358
  return;
1651
2359
  }
1652
2360
  const orderSnapshot = this.normalizeOrderCollectionsForIngress(
@@ -1670,6 +2378,8 @@ var OrderModule = class extends import_BaseModule.BaseModule {
1670
2378
  } else if (typeof this.dbManager.bulkAdd === "function") {
1671
2379
  writeMethod = "bulkAdd";
1672
2380
  await this.dbManager.bulkAdd(INDEXDB_STORE_NAME, [orderSnapshot]);
2381
+ } else if (options.throwOnError) {
2382
+ throw new Error("订单 SQLite 数据库不支持写入");
1673
2383
  }
1674
2384
  this.recordRecentSqliteWrite(source, [orderSnapshot]);
1675
2385
  this.logInfo("updateOrderInSQLite-完成", {
@@ -1683,15 +2393,18 @@ var OrderModule = class extends import_BaseModule.BaseModule {
1683
2393
  error: error instanceof Error ? error.message : String(error),
1684
2394
  orderId: orderSnapshot.order_id ?? null
1685
2395
  });
2396
+ if (options.throwOnError)
2397
+ throw error;
1686
2398
  }
1687
2399
  }
1688
2400
  /**
1689
- * 全量快照替换 orders 表(clear + bulkAdd),用于 SSE 全量拉取与营业日窗口裁剪。
2401
+ * 全量快照替换 orders 表(clear + bulkAdd),并保留本地 draft / need_sync / pending 支付订单。
2402
+ * 用于 SSE 全量拉取与营业日窗口裁剪。
1690
2403
  *
1691
2404
  * @example
1692
2405
  * await this.replaceOrdersSnapshotInSQLite(orderList)
1693
2406
  */
1694
- async replaceOrdersSnapshotInSQLite(orderList, source) {
2407
+ async replaceOrdersSnapshotInSQLite(orderList, source, protectedOrdersBeforeRemoteFetch = []) {
1695
2408
  if (!this.dbManager) {
1696
2409
  return this.compactOrderListByIdentity(
1697
2410
  orderList.map((order) => this.normalizeRemoteSyncedOrder(order)),
@@ -1708,16 +2421,52 @@ var OrderModule = class extends import_BaseModule.BaseModule {
1708
2421
  try {
1709
2422
  await this.runInOrderSQLiteSaveQueue(async () => {
1710
2423
  const existingOrders = typeof this.dbManager.getAll === "function" ? await this.dbManager.getAll(INDEXDB_STORE_NAME) : [];
2424
+ const protectedLocalOrders = protectedOrdersBeforeRemoteFetch.map((order) => this.normalizeOrderCollectionsForIngress(
2425
+ order,
2426
+ `${source}.protectedBeforeRemoteFetch`
2427
+ )).filter((order) => this.shouldProtectLocalOrderFromRemoteSnapshot(order));
2428
+ for (const rawCurrentOrder of existingOrders || []) {
2429
+ const currentOrder = this.normalizeOrderCollectionsForIngress(
2430
+ rawCurrentOrder,
2431
+ `${source}.currentSQLite`
2432
+ );
2433
+ const protectedIndex = this.findOrderIndexByIdentity(
2434
+ protectedLocalOrders,
2435
+ currentOrder
2436
+ );
2437
+ const currentNeedsProtection = this.shouldProtectLocalOrderFromRemoteSnapshot(currentOrder);
2438
+ if (protectedIndex >= 0) {
2439
+ if (currentNeedsProtection) {
2440
+ protectedLocalOrders[protectedIndex] = currentOrder;
2441
+ } else {
2442
+ protectedLocalOrders.splice(protectedIndex, 1);
2443
+ }
2444
+ continue;
2445
+ }
2446
+ if (currentNeedsProtection)
2447
+ protectedLocalOrders.push(currentOrder);
2448
+ }
1711
2449
  const orderListSnapshot = this.mergeRemoteSnapshotWithPendingOrders(
1712
2450
  remoteSnapshot,
1713
- existingOrders || []
2451
+ protectedLocalOrders
1714
2452
  );
1715
2453
  mergedSnapshot = this.compactOrderListByIdentity(orderListSnapshot, `${source}.sqliteSnapshot`).list;
1716
2454
  this.logInfo("replaceOrdersSnapshotInSQLite-开始", {
1717
2455
  source,
1718
2456
  count: mergedSnapshot.length,
1719
2457
  remoteCount: remoteSnapshot.length,
1720
- preservedPendingCount: mergedSnapshot.length - remoteSnapshot.length
2458
+ protectedBeforeRemoteFetchCount: protectedOrdersBeforeRemoteFetch.length,
2459
+ protectedLocalOrderCount: protectedLocalOrders.length,
2460
+ preservedLocalUnsyncedCount: mergedSnapshot.length - remoteSnapshot.length,
2461
+ protectedPendingPaymentCount: protectedLocalOrders.reduce(
2462
+ (count, order) => count + this.getIdentifiedPendingPayments(order).length,
2463
+ 0
2464
+ ),
2465
+ protectedPendingPaymentIdentities: protectedLocalOrders.flatMap((order) => this.getIdentifiedPendingPayments(order).map((payment) => ({
2466
+ order_id: order.order_id ?? null,
2467
+ external_sale_number: order.external_sale_number ?? null,
2468
+ payment_identities: (0, import_payment_utils.getOrderPaymentIdentityKeys)(payment)
2469
+ })))
1721
2470
  });
1722
2471
  await this.dbManager.clear(INDEXDB_STORE_NAME);
1723
2472
  this.logInfo("replaceOrdersSnapshotInSQLite-clear完成", {