@pisell/pisellos 2.2.309 → 2.2.311

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 (122) hide show
  1. package/dist/model/strategy/adapter/promotion/index.js +0 -9
  2. package/dist/model/strategy/adapter/walletPass/evaluator.d.ts +2 -0
  3. package/dist/model/strategy/adapter/walletPass/evaluator.js +4 -2
  4. package/dist/modules/BookingContext/index.js +4 -4
  5. package/dist/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +58 -19
  6. package/dist/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.js +24 -12
  7. package/dist/modules/Customer/index.js +6 -3
  8. package/dist/modules/Customer/types.d.ts +2 -0
  9. package/dist/modules/Discount/availability.d.ts +7 -0
  10. package/dist/modules/Discount/availability.js +49 -0
  11. package/dist/modules/Discount/entitlementPass.d.ts +34 -0
  12. package/dist/modules/Discount/entitlementPass.js +147 -0
  13. package/dist/modules/Discount/index.d.ts +2 -2
  14. package/dist/modules/Discount/index.js +37 -151
  15. package/dist/modules/Discount/types.d.ts +36 -5
  16. package/dist/modules/OpenData/index.d.ts +1 -1
  17. package/dist/modules/OpenData/index.js +21 -12
  18. package/dist/modules/Order/index.d.ts +32 -4
  19. package/dist/modules/Order/index.js +733 -373
  20. package/dist/modules/Order/types.d.ts +17 -0
  21. package/dist/modules/Order/utils/orderCollectionIdentity.js +16 -12
  22. package/dist/modules/Order/utils.d.ts +19 -5
  23. package/dist/modules/Order/utils.js +306 -86
  24. package/dist/modules/Payment/index.d.ts +0 -5
  25. package/dist/modules/Payment/index.js +35 -85
  26. package/dist/modules/Payment/types.d.ts +21 -1
  27. package/dist/modules/Payment/types.js +2 -0
  28. package/dist/modules/Payment/walletpass.d.ts +1 -1
  29. package/dist/modules/Payment/walletpass.js +80 -47
  30. package/dist/modules/ProductList/index.js +18 -5
  31. package/dist/modules/Rules/index.d.ts +2 -2
  32. package/dist/modules/Rules/index.js +480 -186
  33. package/dist/modules/Rules/types.d.ts +4 -0
  34. package/dist/server/index.js +8 -5
  35. package/dist/server/modules/floor-plan/index.js +34 -3
  36. package/dist/server/modules/order/index.js +6 -0
  37. package/dist/server/modules/order/types.d.ts +12 -0
  38. package/dist/server/modules/payment/index.d.ts +25 -6
  39. package/dist/server/modules/payment/index.js +660 -133
  40. package/dist/server/utils/small-ticket.d.ts +8 -5
  41. package/dist/server/utils/small-ticket.js +1269 -199
  42. package/dist/solution/BaseSales/index.d.ts +11 -2
  43. package/dist/solution/BaseSales/index.js +1463 -1173
  44. package/dist/solution/BaseSales/types.d.ts +7 -3
  45. package/dist/solution/BaseSales/utils/cartPromotion.js +141 -126
  46. package/dist/solution/BookingTicket/index.d.ts +11 -2
  47. package/dist/solution/BookingTicket/index.js +663 -542
  48. package/dist/solution/BookingTicket/types.d.ts +5 -1
  49. package/dist/solution/BookingTicket/utils/scan/applyGlobalScan.d.ts +2 -0
  50. package/dist/solution/BookingTicket/utils/scan/applyGlobalScan.js +338 -147
  51. package/dist/solution/BookingTicket/utils/scan/cloudSearch.js +8 -4
  52. package/dist/solution/BookingTicket/utils/scan/formatGlobalScan.d.ts +6 -0
  53. package/dist/solution/BookingTicket/utils/scan/formatGlobalScan.js +19 -6
  54. package/dist/solution/ShopDiscount/index.d.ts +2 -0
  55. package/dist/solution/ShopDiscount/index.js +99 -42
  56. package/dist/solution/ShopDiscount/utils.d.ts +4 -2
  57. package/dist/solution/ShopDiscount/utils.js +43 -23
  58. package/dist/solution/UnifiedBookingSales/index.d.ts +2 -2
  59. package/dist/solution/UnifiedBookingSales/index.js +5 -5
  60. package/dist/utils/os-private-payload.d.ts +5 -0
  61. package/dist/utils/os-private-payload.js +17 -0
  62. package/lib/model/strategy/adapter/walletPass/evaluator.d.ts +2 -0
  63. package/lib/model/strategy/adapter/walletPass/evaluator.js +2 -0
  64. package/lib/modules/BookingContext/index.js +3 -3
  65. package/lib/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +35 -6
  66. package/lib/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.js +13 -6
  67. package/lib/modules/Customer/index.js +5 -1
  68. package/lib/modules/Customer/types.d.ts +2 -0
  69. package/lib/modules/Discount/availability.d.ts +7 -0
  70. package/lib/modules/Discount/availability.js +43 -0
  71. package/lib/modules/Discount/entitlementPass.d.ts +34 -0
  72. package/lib/modules/Discount/entitlementPass.js +141 -0
  73. package/lib/modules/Discount/index.d.ts +2 -2
  74. package/lib/modules/Discount/index.js +23 -129
  75. package/lib/modules/Discount/types.d.ts +36 -5
  76. package/lib/modules/OpenData/index.d.ts +1 -1
  77. package/lib/modules/OpenData/index.js +11 -2
  78. package/lib/modules/Order/index.d.ts +32 -4
  79. package/lib/modules/Order/index.js +341 -71
  80. package/lib/modules/Order/types.d.ts +17 -0
  81. package/lib/modules/Order/utils/orderCollectionIdentity.js +14 -11
  82. package/lib/modules/Order/utils.d.ts +19 -5
  83. package/lib/modules/Order/utils.js +215 -13
  84. package/lib/modules/Payment/index.d.ts +0 -5
  85. package/lib/modules/Payment/index.js +3 -28
  86. package/lib/modules/Payment/types.d.ts +21 -1
  87. package/lib/modules/Payment/types.js +1 -0
  88. package/lib/modules/Payment/walletpass.d.ts +1 -1
  89. package/lib/modules/Payment/walletpass.js +29 -9
  90. package/lib/modules/ProductList/index.js +14 -1
  91. package/lib/modules/Rules/index.d.ts +2 -2
  92. package/lib/modules/Rules/index.js +357 -110
  93. package/lib/modules/Rules/types.d.ts +4 -0
  94. package/lib/server/index.js +8 -5
  95. package/lib/server/modules/floor-plan/index.js +29 -1
  96. package/lib/server/modules/order/index.js +6 -0
  97. package/lib/server/modules/order/types.d.ts +12 -0
  98. package/lib/server/modules/payment/index.d.ts +25 -6
  99. package/lib/server/modules/payment/index.js +350 -78
  100. package/lib/server/utils/small-ticket.d.ts +8 -5
  101. package/lib/server/utils/small-ticket.js +1009 -121
  102. package/lib/solution/BaseSales/index.d.ts +11 -2
  103. package/lib/solution/BaseSales/index.js +164 -30
  104. package/lib/solution/BaseSales/types.d.ts +7 -3
  105. package/lib/solution/BaseSales/utils/cartPromotion.js +12 -4
  106. package/lib/solution/BookingTicket/index.d.ts +11 -2
  107. package/lib/solution/BookingTicket/index.js +103 -41
  108. package/lib/solution/BookingTicket/types.d.ts +5 -1
  109. package/lib/solution/BookingTicket/utils/scan/applyGlobalScan.d.ts +2 -0
  110. package/lib/solution/BookingTicket/utils/scan/applyGlobalScan.js +174 -42
  111. package/lib/solution/BookingTicket/utils/scan/cloudSearch.js +8 -4
  112. package/lib/solution/BookingTicket/utils/scan/formatGlobalScan.d.ts +6 -0
  113. package/lib/solution/BookingTicket/utils/scan/formatGlobalScan.js +11 -3
  114. package/lib/solution/ShopDiscount/index.d.ts +2 -0
  115. package/lib/solution/ShopDiscount/index.js +55 -9
  116. package/lib/solution/ShopDiscount/utils.d.ts +4 -2
  117. package/lib/solution/ShopDiscount/utils.js +42 -25
  118. package/lib/solution/UnifiedBookingSales/index.d.ts +2 -2
  119. package/lib/solution/UnifiedBookingSales/index.js +5 -5
  120. package/lib/utils/os-private-payload.d.ts +5 -0
  121. package/lib/utils/os-private-payload.js +22 -0
  122. package/package.json +1 -1
@@ -5,18 +5,33 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.PaymentServerModule = void 0;
7
7
  var _BaseModule = require("../../../modules/BaseModule");
8
+ var _lodashEs = require("lodash-es");
8
9
  const WALLET_PASS_CODE = 'WALLET_PASS';
9
10
  const WALLET_DEPENDENT_CODES = new Set(['PRODUCTVOUCHER', 'GIFTCARD', 'POINTCARD']);
10
11
  const PAYMENT_METHODS_LOADED_EVENT = 'payment:onPaymentMethodsLoaded';
12
+ const PAYMENT_METHODS_CHANGED_EVENT = 'payment:onPaymentMethodsChanged';
13
+ const PAYMENT_METHOD_STORE_NAME = 'pay_method';
14
+ const CUSTOM_PAYMENT_MODULE = 'custom_payment';
15
+ const PAYMENT_METHOD_SYNC_DEBOUNCE_MS = 500;
16
+ const SNAPSHOT_ORDER_FIELD = '__payment_snapshot_order';
17
+ const SNAPSHOT_SHOP_ID_FIELD = '__payment_snapshot_shop_id';
11
18
  class PaymentServerModule extends _BaseModule.BaseModule {
12
19
  defaultName = 'payment';
13
20
  defaultVersion = '1.0.0';
14
21
  request;
15
22
  app;
16
23
  logger;
24
+ dbManager;
17
25
  store;
18
- payMethodMemoryCache = null;
19
- payMethodListInFlight = null;
26
+ paymentDataSource;
27
+ initialLoadInFlight = null;
28
+ refreshInFlight = null;
29
+ hasLoadedPayMethods = false;
30
+ hasEmittedLoaded = false;
31
+ syncTimer;
32
+ pendingMessageUuids = new Set();
33
+ queuedTrailingRefresh = false;
34
+ trailingMessageUuids = new Set();
20
35
  constructor(name, version) {
21
36
  super(name || 'payment', version);
22
37
  }
@@ -31,78 +46,327 @@ class PaymentServerModule extends _BaseModule.BaseModule {
31
46
  const appPlugin = core.getPlugin('app');
32
47
  this.app = appPlugin?.getApp();
33
48
  this.logger = this.app?.logger;
49
+ this.dbManager = this.app?.sqlite;
34
50
  if (!this.request && !this.app?.request) {
35
51
  throw new Error('PaymentServerModule 需要 request 插件或 app.request 支持');
36
52
  }
53
+ this.initPaymentDataSource();
54
+ this.setupPaymentMethodSync();
37
55
  this.logInfo('PaymentServerModule initialized successfully', {
38
- hasAppRequest: !!this.app?.request
56
+ hasAppRequest: !!this.app?.request,
57
+ hasSQLite: !!this.dbManager,
58
+ hasPaymentDataSource: !!this.paymentDataSource
39
59
  });
40
60
  }
41
-
42
- // getRoutes(): RouteDefinition[] {
43
- // return [
44
- // {
45
- // method: 'get',
46
- // path: '/pay/custom-payment/all',
47
- // handler: async ({ data, config }) => {
48
- // const payMethods = await this.getPayMethodListAsync({
49
- // shouldFilter: this.shouldFilterPayMethods(data, config),
50
- // });
51
- // return {
52
- // code: 200,
53
- // status: true,
54
- // data: payMethods,
55
- // };
56
- // },
57
- // },
58
- // ];
59
- // }
60
-
61
+ async preload() {
62
+ try {
63
+ await this.ensureInitialPayMethods('preload');
64
+ } catch (error) {
65
+ this.logError('preload 支付方式失败,保持空列表并允许后续读取重试', {
66
+ error: this.getErrorMessage(error)
67
+ });
68
+ }
69
+ }
61
70
  async getPayMethodListAsync(options = {}) {
62
- this.logInfo('Starting getPayMethodListAsync');
63
- if (this.payMethodMemoryCache?.length) {
64
- return this.resolvePayMethods(this.payMethodMemoryCache, options);
71
+ this.logInfo('Starting getPayMethodListAsync', {
72
+ hasLoadedPayMethods: this.hasLoadedPayMethods,
73
+ cachedCount: this.store.payMethods.length
74
+ });
75
+ if (!this.hasLoadedPayMethods) {
76
+ try {
77
+ await this.ensureInitialPayMethods('route');
78
+ } catch (error) {
79
+ this.logError('getPayMethodListAsync 初始化失败', {
80
+ error: this.getErrorMessage(error)
81
+ });
82
+ }
83
+ } else if (!this.paymentDataSource) {
84
+ try {
85
+ await this.refreshPayMethodsSingleFlight({
86
+ trigger: 'compat.route',
87
+ emitChanged: false
88
+ });
89
+ } catch (error) {
90
+ this.logWarning('兼容模式远端刷新失败,保留当前运行态快照', {
91
+ error: this.getErrorMessage(error)
92
+ });
93
+ }
65
94
  }
66
- if (this.payMethodListInFlight) {
67
- const payMethods = await this.payMethodListInFlight;
68
- return this.resolvePayMethods(payMethods, options);
95
+ return this.resolvePayMethods(this.store.payMethods, options);
96
+ }
97
+ async ensureInitialPayMethods(trigger) {
98
+ if (this.hasLoadedPayMethods) return this.store.payMethods;
99
+ if (this.initialLoadInFlight) return this.initialLoadInFlight;
100
+ this.initialLoadInFlight = (async () => {
101
+ const localSnapshot = await this.loadPayMethodsFromSQLite();
102
+ if (localSnapshot.available) {
103
+ this.setPayMethodMirror(localSnapshot.methods);
104
+ this.logInfo('从 SQLite 加载支付方式兜底快照', {
105
+ trigger,
106
+ count: localSnapshot.methods.length,
107
+ shopScope: this.resolveCurrentShopScope(localSnapshot.methods)
108
+ });
109
+ }
110
+ try {
111
+ await this.refreshPayMethodsSingleFlight({
112
+ trigger,
113
+ emitChanged: false
114
+ });
115
+ this.hasLoadedPayMethods = true;
116
+ } catch (error) {
117
+ if (!localSnapshot.available) throw error;
118
+ this.hasLoadedPayMethods = true;
119
+ this.logWarning('远端刷新失败,继续使用 SQLite 支付方式快照', {
120
+ trigger,
121
+ count: localSnapshot.methods.length,
122
+ error: this.getErrorMessage(error)
123
+ });
124
+ }
125
+ if (!this.hasEmittedLoaded) {
126
+ this.hasEmittedLoaded = true;
127
+ await this.core.effects.emit(PAYMENT_METHODS_LOADED_EVENT, this.store.payMethods);
128
+ }
129
+ return this.store.payMethods;
130
+ })().finally(() => {
131
+ this.initialLoadInFlight = null;
132
+ });
133
+ return this.initialLoadInFlight;
134
+ }
135
+ initPaymentDataSource() {
136
+ const PaymentDataSourceClass = this.core.serverOptions?.All_DATA_SOURCES?.PaymentDataSource;
137
+ if (!PaymentDataSourceClass) {
138
+ this.logWarning('PaymentDataSource 不可用,使用无本地持久化的兼容模式');
139
+ return;
69
140
  }
70
- this.payMethodListInFlight = this.loadPayMethodListAsync().finally(() => {
71
- this.payMethodListInFlight = null;
141
+ this.paymentDataSource = new PaymentDataSourceClass();
142
+ }
143
+ setupPaymentMethodSync() {
144
+ if (!this.paymentDataSource) return;
145
+ this.paymentDataSource.run({
146
+ pubsub: {
147
+ callback: res => {
148
+ const message = res?.data || res;
149
+ if (!message || message.module !== CUSTOM_PAYMENT_MODULE) return;
150
+ const messageUuid = String(message.message_uuid || '').trim();
151
+ if (messageUuid) this.pendingMessageUuids.add(messageUuid);
152
+ this.logInfo('收到 custom_payment Ably 消息,等待全量刷新', {
153
+ message_uuid: messageUuid || undefined,
154
+ action: message.action,
155
+ operation: message.operation
156
+ });
157
+ this.schedulePaymentMethodRefresh();
158
+ }
159
+ }
160
+ }).catch(error => {
161
+ this.logError('建立 custom_payment pubsub 订阅失败', {
162
+ error: this.getErrorMessage(error)
163
+ });
72
164
  });
73
- const payMethods = await this.payMethodListInFlight;
74
- return this.resolvePayMethods(payMethods, options);
75
165
  }
76
- clearPayMethodCache() {
77
- this.payMethodMemoryCache = null;
78
- this.store.payMethods = [];
166
+ schedulePaymentMethodRefresh() {
167
+ if (this.syncTimer) clearTimeout(this.syncTimer);
168
+ this.syncTimer = setTimeout(() => {
169
+ this.syncTimer = undefined;
170
+ const messageUuids = [...this.pendingMessageUuids];
171
+ this.pendingMessageUuids.clear();
172
+ void this.refreshPayMethodsSingleFlight({
173
+ trigger: 'ably',
174
+ emitChanged: true,
175
+ messageUuids
176
+ }).catch(error => {
177
+ this.logError('custom_payment Ably 全量刷新失败,保留旧快照', {
178
+ message_uuids: messageUuids,
179
+ error: this.getErrorMessage(error)
180
+ });
181
+ });
182
+ }, PAYMENT_METHOD_SYNC_DEBOUNCE_MS);
79
183
  }
80
- async loadPayMethodListAsync() {
184
+ async refreshPayMethodsSingleFlight(context) {
185
+ if (this.refreshInFlight) {
186
+ if (context.trigger.startsWith('ably')) {
187
+ this.queuedTrailingRefresh = true;
188
+ context.messageUuids?.forEach(uuid => this.trailingMessageUuids.add(uuid));
189
+ this.logInfo('支付方式刷新进行中,已排队一次尾随刷新', {
190
+ trigger: context.trigger,
191
+ message_uuids: context.messageUuids || []
192
+ });
193
+ }
194
+ return this.refreshInFlight;
195
+ }
196
+ const refreshTask = this.performRemoteRefresh(context);
197
+ this.refreshInFlight = refreshTask;
81
198
  try {
82
- const payMethods = await this.fetchRemotePayMethods();
83
- this.setPayMethodCache(payMethods);
84
- await this.core.effects.emit(PAYMENT_METHODS_LOADED_EVENT, payMethods);
85
- this.logInfo('getPayMethodListAsync completed successfully', {
86
- payMethods
199
+ return await refreshTask;
200
+ } finally {
201
+ if (this.refreshInFlight === refreshTask) this.refreshInFlight = null;
202
+ if (this.queuedTrailingRefresh) {
203
+ this.queuedTrailingRefresh = false;
204
+ const messageUuids = [...this.trailingMessageUuids];
205
+ this.trailingMessageUuids.clear();
206
+ void this.refreshPayMethodsSingleFlight({
207
+ trigger: 'ably.trailing',
208
+ emitChanged: true,
209
+ messageUuids
210
+ }).catch(error => {
211
+ this.logError('custom_payment 尾随刷新失败,保留当前快照', {
212
+ message_uuids: messageUuids,
213
+ error: this.getErrorMessage(error)
214
+ });
215
+ });
216
+ }
217
+ }
218
+ }
219
+ async performRemoteRefresh(context) {
220
+ const oldMethods = this.store.payMethods;
221
+ const payMethods = await this.fetchRemotePayMethods();
222
+ const persisted = await this.replacePayMethodsInSQLite(payMethods);
223
+ if (!persisted) {
224
+ this.logWarning('SQLite 不可用,支付方式仅用于当前运行时', {
225
+ trigger: context.trigger,
226
+ count: payMethods.length
87
227
  });
88
- return payMethods;
89
- } catch (error) {
90
- console.error('[PaymentServerModule] 获取支付方式列表失败', error);
91
- this.logError('getPayMethodListAsync failed', error);
92
- return [];
93
228
  }
229
+ this.setPayMethodMirror(payMethods);
230
+ this.hasLoadedPayMethods = true;
231
+ const changed = !this.arePayMethodsEqual(oldMethods, payMethods);
232
+ if (context.emitChanged && changed) {
233
+ await this.core.effects.emit(PAYMENT_METHODS_CHANGED_EVENT, {
234
+ oldMethods,
235
+ newMethods: this.store.payMethods
236
+ });
237
+ }
238
+ this.logInfo('远端支付方式全量刷新成功', {
239
+ trigger: context.trigger,
240
+ message_uuids: context.messageUuids || [],
241
+ count: payMethods.length,
242
+ persisted,
243
+ changed
244
+ });
245
+ return this.store.payMethods;
94
246
  }
95
247
  async fetchRemotePayMethods() {
96
- const requester = this.app?.request || this.request;
97
- const response = await requester.get('/shop/pay/custom-payment/all', undefined);
98
- const payMethods = response?.data || response || [];
99
- return Array.isArray(payMethods) ? payMethods : [];
248
+ const response = this.paymentDataSource ? await this.paymentDataSource.run({
249
+ http: {
250
+ url: '/shop/pay/custom-payment/all'
251
+ }
252
+ }) : await (this.app?.request || this.request).get('/shop/pay/custom-payment/all', undefined);
253
+ const payMethods = response?.data !== undefined ? response.data : response;
254
+ if (!Array.isArray(payMethods)) {
255
+ throw new Error('支付方式全量接口返回值不是数组');
256
+ }
257
+ if (payMethods.some(method => !method || method.id === undefined || method.id === null)) {
258
+ throw new Error('支付方式全量接口存在缺少 id 的记录');
259
+ }
260
+ return payMethods;
261
+ }
262
+ async loadPayMethodsFromSQLite() {
263
+ if (!this.dbManager) return {
264
+ available: false,
265
+ methods: []
266
+ };
267
+ try {
268
+ const rows = await this.dbManager.getAll(PAYMENT_METHOD_STORE_NAME);
269
+ if (!Array.isArray(rows) || rows.length === 0) {
270
+ return {
271
+ available: false,
272
+ methods: []
273
+ };
274
+ }
275
+ const currentShopScope = this.resolveCurrentShopScope();
276
+ if (!currentShopScope) {
277
+ this.logWarning('无法确认当前店铺,忽略 SQLite 支付方式快照');
278
+ return {
279
+ available: false,
280
+ methods: []
281
+ };
282
+ }
283
+ const scopedRows = rows.filter(row => String(row?.[SNAPSHOT_SHOP_ID_FIELD] || '') === currentShopScope);
284
+ if (scopedRows.length === 0) {
285
+ this.logWarning('忽略其他店铺的 SQLite 支付方式快照', {
286
+ currentShopScope,
287
+ storedShopScopes: [...new Set(rows.map(row => String(row?.[SNAPSHOT_SHOP_ID_FIELD] || '')).filter(Boolean))]
288
+ });
289
+ return {
290
+ available: false,
291
+ methods: []
292
+ };
293
+ }
294
+ const methods = scopedRows.slice().sort((left, right) => Number(left?.[SNAPSHOT_ORDER_FIELD] || 0) - Number(right?.[SNAPSHOT_ORDER_FIELD] || 0)).map(row => this.stripSnapshotFields(row));
295
+ return {
296
+ available: true,
297
+ methods
298
+ };
299
+ } catch (error) {
300
+ this.logWarning('从 SQLite 读取支付方式失败', {
301
+ error: this.getErrorMessage(error)
302
+ });
303
+ return {
304
+ available: false,
305
+ methods: []
306
+ };
307
+ }
308
+ }
309
+ async replacePayMethodsInSQLite(payMethods) {
310
+ if (!this.dbManager) return false;
311
+ const previousRows = await this.dbManager.getAll(PAYMENT_METHOD_STORE_NAME);
312
+ const shopScope = this.resolveCurrentShopScope(payMethods);
313
+ const snapshotRows = payMethods.map((method, index) => ({
314
+ ...method,
315
+ [SNAPSHOT_ORDER_FIELD]: index,
316
+ [SNAPSHOT_SHOP_ID_FIELD]: shopScope
317
+ }));
318
+ try {
319
+ await this.dbManager.clear(PAYMENT_METHOD_STORE_NAME);
320
+ if (snapshotRows.length > 0) {
321
+ await this.dbManager.bulkAdd(PAYMENT_METHOD_STORE_NAME, snapshotRows);
322
+ }
323
+ return true;
324
+ } catch (error) {
325
+ const writeError = this.getErrorMessage(error);
326
+ try {
327
+ await this.dbManager.clear(PAYMENT_METHOD_STORE_NAME);
328
+ if (Array.isArray(previousRows) && previousRows.length > 0) {
329
+ await this.dbManager.bulkAdd(PAYMENT_METHOD_STORE_NAME, previousRows);
330
+ }
331
+ } catch (rollbackError) {
332
+ this.logError('支付方式 SQLite 快照回滚失败', {
333
+ writeError,
334
+ rollbackError: this.getErrorMessage(rollbackError)
335
+ });
336
+ }
337
+ throw new Error(`支付方式 SQLite 全量替换失败: ${writeError}`);
338
+ }
339
+ }
340
+ resolveCurrentShopScope(payMethods = []) {
341
+ const readStorageObject = key => {
342
+ try {
343
+ const raw = this.app?.storage?.getStorage?.(key);
344
+ if (!raw) return {};
345
+ if (typeof raw === 'object') return raw;
346
+ const parsed = JSON.parse(raw);
347
+ return parsed && typeof parsed === 'object' ? parsed : {};
348
+ } catch {
349
+ return {};
350
+ }
351
+ };
352
+ const selectedStore = readStorageObject('selectedStore');
353
+ const core = readStorageObject('core');
354
+ const scope = selectedStore.id ?? selectedStore.shop_id ?? core.id ?? core.shop_id ?? payMethods.find(method => method?.shop_id != null)?.shop_id;
355
+ return scope === undefined || scope === null ? '' : String(scope);
356
+ }
357
+ stripSnapshotFields(row) {
358
+ const {
359
+ [SNAPSHOT_ORDER_FIELD]: _snapshotOrder,
360
+ [SNAPSHOT_SHOP_ID_FIELD]: _snapshotShopId,
361
+ ...method
362
+ } = row;
363
+ return method;
100
364
  }
101
- shouldFilterPayMethods(data, config) {
102
- return data?.filterPaymentMethods === true || config?.filterPaymentMethods === true;
365
+ arePayMethodsEqual(left, right) {
366
+ return (0, _lodashEs.isEqual)(left, right);
103
367
  }
104
368
  resolvePayMethods(payMethods, options) {
105
- if (!options.shouldFilter) return payMethods;
369
+ if (!options.shouldFilter) return [...payMethods];
106
370
  return this.filterPayMethods(payMethods);
107
371
  }
108
372
  filterPayMethods(payMethods) {
@@ -121,48 +385,56 @@ class PaymentServerModule extends _BaseModule.BaseModule {
121
385
  });
122
386
  return availableMethods;
123
387
  }
124
- setPayMethodCache(payMethods) {
125
- this.payMethodMemoryCache = payMethods;
126
- this.store.payMethods = payMethods;
388
+ setPayMethodMirror(payMethods) {
389
+ this.store.payMethods = payMethods.map(method => ({
390
+ ...method
391
+ }));
392
+ }
393
+ getErrorMessage(error) {
394
+ return error instanceof Error ? error.message : String(error);
127
395
  }
128
396
  logInfo(title, metadata) {
129
397
  try {
130
- if (this.logger) {
131
- this.logger.addLog({
132
- type: 'info',
133
- title: `[PaymentServerModule] ${title}`,
134
- metadata: metadata || {}
135
- });
136
- }
398
+ this.logger?.addLog({
399
+ type: 'info',
400
+ title: `[PaymentServerModule] ${title}`,
401
+ metadata: metadata || {}
402
+ });
137
403
  } catch {
138
404
  // 日志记录失败不影响主流程
139
405
  }
140
406
  }
141
407
  logWarning(title, metadata) {
142
408
  try {
143
- if (this.logger) {
144
- this.logger.addLog({
145
- type: 'warning',
146
- title: `[PaymentServerModule] ${title}`,
147
- metadata: metadata || {}
148
- });
149
- }
409
+ this.logger?.addLog({
410
+ type: 'warning',
411
+ title: `[PaymentServerModule] ${title}`,
412
+ metadata: metadata || {}
413
+ });
150
414
  } catch {
151
415
  // 日志记录失败不影响主流程
152
416
  }
153
417
  }
154
418
  logError(title, metadata) {
155
419
  try {
156
- if (this.logger) {
157
- this.logger.addLog({
158
- type: 'error',
159
- title: `[PaymentServerModule] ${title}`,
160
- metadata: metadata || {}
161
- });
162
- }
420
+ this.logger?.addLog({
421
+ type: 'error',
422
+ title: `[PaymentServerModule] ${title}`,
423
+ metadata: metadata || {}
424
+ });
163
425
  } catch {
164
426
  // 日志记录失败不影响主流程
165
427
  }
166
428
  }
429
+ destroy() {
430
+ if (this.syncTimer) clearTimeout(this.syncTimer);
431
+ this.syncTimer = undefined;
432
+ this.pendingMessageUuids.clear();
433
+ this.trailingMessageUuids.clear();
434
+ this.queuedTrailingRefresh = false;
435
+ this.paymentDataSource?.destroy?.();
436
+ this.paymentDataSource = undefined;
437
+ super.destroy();
438
+ }
167
439
  }
168
440
  exports.PaymentServerModule = PaymentServerModule;
@@ -28,32 +28,35 @@ export interface SmallTicketItem {
28
28
  value: string;
29
29
  size?: number;
30
30
  align?: string;
31
+ product_uid?: string | number;
32
+ booking_uid?: string | number;
31
33
  }
32
34
  export interface SmallTicketProduct {
33
35
  product_title: string;
34
36
  product_quantity: number;
35
37
  selling_price: string;
38
+ total_selling_price: string;
36
39
  original_price: string;
37
40
  base_price?: string;
38
41
  total_original_price: string;
42
+ options_total_price?: string;
43
+ package_items_total_price?: string;
39
44
  extension_list: any[];
40
45
  product_uid?: string | number;
41
46
  booking_uid?: string | number;
42
47
  product_note?: string;
48
+ allergy?: string;
43
49
  options?: Array<{
44
50
  name: string;
45
51
  num?: number;
46
52
  price?: string;
53
+ total_price: string;
47
54
  value?: string;
48
55
  }>;
49
56
  variant?: string;
50
57
  combinations?: any[];
51
58
  }
52
- export interface SmallTicketBookingSnapshot extends Record<string, any> {
53
- product_uid?: string | number;
54
- booking_uid?: string | number;
55
- }
56
- export type SmallTicketAppointment = SmallTicketItem[] | SmallTicketBookingSnapshot[];
59
+ export type SmallTicketAppointment = SmallTicketItem[];
57
60
  export interface SmallTicketLocaleData {
58
61
  base_data: Record<string, any>;
59
62
  products: SmallTicketProduct[];