@pisell/pisellos 2.2.285 → 2.2.286

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.
@@ -608,6 +608,7 @@ export interface SyncPaymentsToOrderParams {
608
608
  businessCode?: string;
609
609
  channel?: string;
610
610
  confirmPendingVoucherPayments?: boolean;
611
+ enhancePayload?: SubmitPayloadEnhancer;
611
612
  }
612
613
  export interface SyncPaymentsToOrderResult<T = any> {
613
614
  isFullyPaid: boolean;
@@ -505,6 +505,10 @@ declare class Server {
505
505
  private buildCheckoutTaskIdempotencyKey;
506
506
  private isBlankCheckoutValue;
507
507
  private isLocalCheckoutOrderId;
508
+ private applyCheckoutFulfillmentBuzzer;
509
+ private buildCheckoutResourceIdentifierBuzzer;
510
+ private getCheckoutResourceIdentifier;
511
+ private stringifyCheckoutResourceIdentifier;
508
512
  private normalizeCheckoutSubmitData;
509
513
  private ensureCheckoutOrderNumbers;
510
514
  private dispatchCheckoutSyncTask;
@@ -40,6 +40,8 @@ Object.keys(_modules).forEach(function (key) {
40
40
  });
41
41
  });
42
42
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
43
+ const OS_FULFILLMENT_REF_MODE_FIELD = '_os_fulfillment_ref_mode';
44
+
43
45
  // 重新导出类型供外部使用
44
46
 
45
47
  /** 商品查询订阅者 */
@@ -614,7 +616,8 @@ class Server {
614
616
  title
615
617
  } = params;
616
618
  const normalizedData = await this.normalizeCheckoutSubmitData(params.data, title);
617
- const checkoutData = await this.ensureCheckoutOrderNumbers(normalizedData, title);
619
+ const numberedData = await this.ensureCheckoutOrderNumbers(normalizedData, title);
620
+ const checkoutData = this.applyCheckoutFulfillmentBuzzer(numberedData, title);
618
621
  const remoteCheckoutData = this.buildRemoteCheckoutData(checkoutData, title);
619
622
  if (!this.app?.request?.post) {
620
623
  throw new Error('app.request 不可用');
@@ -683,6 +686,7 @@ class Server {
683
686
  const next = (0, _lodashEs.cloneDeep)(this.omitCheckoutSyncMode(data));
684
687
  if (!next || typeof next !== 'object') return next;
685
688
  const record = next;
689
+ delete record[OS_FULFILLMENT_REF_MODE_FIELD];
686
690
  if (!Array.isArray(record.payments)) return next;
687
691
  const originalPayments = record.payments;
688
692
  const filteredPayments = originalPayments.filter(payment => !(0, _paymentUtils.isPendingOrderPayment)(payment));
@@ -3342,6 +3346,82 @@ class Server {
3342
3346
  isLocalCheckoutOrderId(orderId) {
3343
3347
  return typeof orderId === 'string' && orderId.startsWith('LOCAL_');
3344
3348
  }
3349
+ applyCheckoutFulfillmentBuzzer(data, title) {
3350
+ if (!data || typeof data !== 'object') return data;
3351
+ const next = {
3352
+ ...data
3353
+ };
3354
+ const refMode = String(next[OS_FULFILLMENT_REF_MODE_FIELD] || '').trim();
3355
+ delete next[OS_FULFILLMENT_REF_MODE_FIELD];
3356
+ if (!refMode) return next;
3357
+ if (refMode === 'manual_input') {
3358
+ this.logInfo(`${title}: fulfillment ref mode 保留手动 buzzer`, {
3359
+ fulfillment_ref_mode: refMode,
3360
+ has_buzzer: !this.isBlankCheckoutValue(next.buzzer)
3361
+ });
3362
+ return next;
3363
+ }
3364
+ if (refMode === 'resource_identifier') {
3365
+ next.buzzer = this.buildCheckoutResourceIdentifierBuzzer(next.bookings);
3366
+ } else if (refMode === 'sale_number') {
3367
+ next.buzzer = this.isBlankCheckoutValue(next.shop_order_number) ? '' : String(next.shop_order_number);
3368
+ } else if (refMode === 'short_number') {
3369
+ next.buzzer = this.isBlankCheckoutValue(next.shop_full_order_number) ? '' : String(next.shop_full_order_number);
3370
+ } else {
3371
+ this.logWarning(`${title}: 未识别的 fulfillment ref mode,跳过 buzzer 自动填充`, {
3372
+ fulfillment_ref_mode: refMode
3373
+ });
3374
+ return next;
3375
+ }
3376
+ this.logInfo(`${title}: fulfillment ref mode 已填充 buzzer`, {
3377
+ fulfillment_ref_mode: refMode,
3378
+ buzzer: next.buzzer
3379
+ });
3380
+ return next;
3381
+ }
3382
+ buildCheckoutResourceIdentifierBuzzer(bookings) {
3383
+ if (!Array.isArray(bookings)) return '';
3384
+ const values = [];
3385
+ const seen = new Set();
3386
+ const visitResource = resource => {
3387
+ const identifier = this.getCheckoutResourceIdentifier(resource);
3388
+ if (identifier && !seen.has(identifier)) {
3389
+ seen.add(identifier);
3390
+ values.push(identifier);
3391
+ }
3392
+ if (Array.isArray(resource?.children)) {
3393
+ resource.children.forEach(visitResource);
3394
+ }
3395
+ };
3396
+ bookings.forEach(booking => {
3397
+ if (Array.isArray(booking?.resources)) {
3398
+ booking.resources.forEach(visitResource);
3399
+ }
3400
+ });
3401
+ return values.join(',');
3402
+ }
3403
+ getCheckoutResourceIdentifier(resource) {
3404
+ if (!resource || typeof resource !== 'object') return '';
3405
+ const candidates = [resource.main_field, resource.name, resource.title, resource.metadata?.resource_name, resource.metadata?.name, resource.relation_id, resource.id];
3406
+ for (const candidate of candidates) {
3407
+ const value = this.stringifyCheckoutResourceIdentifier(candidate);
3408
+ if (value) return value;
3409
+ }
3410
+ return '';
3411
+ }
3412
+ stringifyCheckoutResourceIdentifier(value) {
3413
+ if (value === undefined || value === null) return '';
3414
+ if (typeof value === 'string' || typeof value === 'number') {
3415
+ return String(value).trim();
3416
+ }
3417
+ if (typeof value !== 'object' || Array.isArray(value)) return '';
3418
+ const localeCandidates = [value.original, value.auto, value.en, value['zh-CN'], value.zh_CN, value['zh-HK'], value.zh_HK];
3419
+ for (const candidate of localeCandidates) {
3420
+ const normalized = this.stringifyCheckoutResourceIdentifier(candidate);
3421
+ if (normalized) return normalized;
3422
+ }
3423
+ return '';
3424
+ }
3345
3425
  async normalizeCheckoutSubmitData(data, title) {
3346
3426
  if (!data || typeof data !== 'object') return data;
3347
3427
  const next = {
@@ -3898,7 +3978,8 @@ class Server {
3898
3978
  });
3899
3979
  }
3900
3980
  const normalizedData = await this.normalizeCheckoutSubmitData(data, title);
3901
- const checkoutData = await this.ensureCheckoutOrderNumbers(normalizedData, title);
3981
+ const numberedData = await this.ensureCheckoutOrderNumbers(normalizedData, title);
3982
+ const checkoutData = this.applyCheckoutFulfillmentBuzzer(numberedData, title);
3902
3983
  const pendingResult = await this.handlePendingSyncCheckoutOrder({
3903
3984
  backendPath,
3904
3985
  data: checkoutData,
@@ -20,6 +20,8 @@ export declare class OrderModule extends BaseModule implements Module {
20
20
  private storage;
21
21
  private orderSQLiteSaveQueue; /** 按 storageKey 记录最近一次 SQLite 写入来源与时间,用于去重 */
22
22
  private recentSqliteWriteByStorageKey;
23
+ /** 最近一次清理过期 SQLite 去重记录的时间,避免每次写入都遍历 Map */
24
+ private lastRecentSqliteWriteCleanupAtMs;
23
25
  /** 最近一次 SSE 全量拉取完成时间 */
24
26
  private lastServerFullFetchAtMs;
25
27
  constructor(name?: string, version?: string);
@@ -58,6 +60,12 @@ export declare class OrderModule extends BaseModule implements Module {
58
60
  */
59
61
  private mergeAuthoritativeOrderCollection;
60
62
  private reconcileOverwriteOrderCollections;
63
+ /**
64
+ * 惰性清理已经超过去重窗口的 SQLite 写入记录。
65
+ *
66
+ * 清理频率与去重窗口一致,既限制 Map 长期增长,也不会移除仍参与去重的记录。
67
+ */
68
+ private cleanupExpiredRecentSqliteWrites;
61
69
  /**
62
70
  * 记录订单最近一次 SQLite 写入,供 pubsub 回声去重使用。
63
71
  *
@@ -56,6 +56,8 @@ class OrderModule extends _BaseModule.BaseModule {
56
56
  storage;
57
57
  orderSQLiteSaveQueue = Promise.resolve(); /** 按 storageKey 记录最近一次 SQLite 写入来源与时间,用于去重 */
58
58
  recentSqliteWriteByStorageKey = new Map();
59
+ /** 最近一次清理过期 SQLite 去重记录的时间,避免每次写入都遍历 Map */
60
+ lastRecentSqliteWriteCleanupAtMs = 0;
59
61
  /** 最近一次 SSE 全量拉取完成时间 */
60
62
  lastServerFullFetchAtMs = 0;
61
63
  constructor(name, version) {
@@ -285,6 +287,24 @@ class OrderModule extends _BaseModule.BaseModule {
285
287
  return this.normalizeOrderCollectionsForIngress((0, _orderCollectionIdentity.applyOrderCollectionUidRemaps)(reconciled, uidRemaps), 'overwriteExistingOrder.reconciled');
286
288
  }
287
289
 
290
+ /**
291
+ * 惰性清理已经超过去重窗口的 SQLite 写入记录。
292
+ *
293
+ * 清理频率与去重窗口一致,既限制 Map 长期增长,也不会移除仍参与去重的记录。
294
+ */
295
+ cleanupExpiredRecentSqliteWrites(now) {
296
+ const elapsedSinceCleanup = now - this.lastRecentSqliteWriteCleanupAtMs;
297
+ if (this.lastRecentSqliteWriteCleanupAtMs > 0 && elapsedSinceCleanup >= 0 && elapsedSinceCleanup < ORDER_SQLITE_DEDUPE_MS) {
298
+ return;
299
+ }
300
+ for (const [storageKey, recentWrite] of this.recentSqliteWriteByStorageKey) {
301
+ if (now - recentWrite.ts >= ORDER_SQLITE_DEDUPE_MS) {
302
+ this.recentSqliteWriteByStorageKey.delete(storageKey);
303
+ }
304
+ }
305
+ this.lastRecentSqliteWriteCleanupAtMs = now;
306
+ }
307
+
288
308
  /**
289
309
  * 记录订单最近一次 SQLite 写入,供 pubsub 回声去重使用。
290
310
  *
@@ -293,6 +313,7 @@ class OrderModule extends _BaseModule.BaseModule {
293
313
  */
294
314
  recordRecentSqliteWrite(source, orders) {
295
315
  const now = Date.now();
316
+ this.cleanupExpiredRecentSqliteWrites(now);
296
317
  for (const order of orders) {
297
318
  const storageKey = this.getOrderStorageKey(order);
298
319
  if (!storageKey) continue;
@@ -1176,6 +1197,8 @@ class OrderModule extends _BaseModule.BaseModule {
1176
1197
  }
1177
1198
  this.isProcessingSyncBatch = false;
1178
1199
  this.pendingSyncMessages = [];
1200
+ this.recentSqliteWriteByStorageKey.clear();
1201
+ this.lastRecentSqliteWriteCleanupAtMs = 0;
1179
1202
  if (this.orderDataSource?.destroy) this.orderDataSource.destroy();
1180
1203
  super.destroy();
1181
1204
  }
@@ -136,6 +136,9 @@ export declare class BaseSalesImpl extends BaseModule implements Module {
136
136
  private getAuthoritativeBundleUnitPrice;
137
137
  private mergeProductForPriceQuery;
138
138
  protected getSubmitOrderSalesChannel(): string | undefined;
139
+ private getFulfillmentRefModeConfigs;
140
+ private getFulfillmentRefMode;
141
+ private buildSubmitPayloadEnhancerWithFulfillmentRefMode;
139
142
  /**
140
143
  * 工厂入口:根据子模块名实例化对应模块。
141
144
  * 默认使用 BookingByStep 公共 `createModule`。子类若需引入额外类型(如 customer),
@@ -34,6 +34,7 @@ var _transformBaseProductToOrderProduct = require("./utils/transformBaseProductT
34
34
  var _quotationPrice = require("./utils/quotationPrice");
35
35
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
36
36
  const SERVER_ORDER_CHANGED_EVENT = 'order:onOrdersChanged';
37
+ const OS_FULFILLMENT_REF_MODE_FIELD = '_os_fulfillment_ref_mode';
37
38
  const WALLET_PAYMENT_CODE_BY_TAG = {
38
39
  product_voucher: 'PRODUCTVOUCHER',
39
40
  gift_card: 'GIFTCARD',
@@ -845,6 +846,30 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
845
846
  getSubmitOrderSalesChannel() {
846
847
  return this.otherParams?.channel;
847
848
  }
849
+ getFulfillmentRefModeConfigs() {
850
+ const candidates = [this.otherParams?.openData, this.otherParams?.dineInConfig, this.store?.openData?.getOpenData?.()];
851
+ return candidates.filter(candidate => candidate && typeof candidate === 'object' && !Array.isArray(candidate));
852
+ }
853
+ getFulfillmentRefMode() {
854
+ for (const config of this.getFulfillmentRefModeConfigs()) {
855
+ const mode = config['fulfillment.fulfillment_ref_mode'];
856
+ if (mode === undefined || mode === null) continue;
857
+ const normalized = String(mode).trim();
858
+ if (normalized) return normalized;
859
+ }
860
+ return undefined;
861
+ }
862
+ buildSubmitPayloadEnhancerWithFulfillmentRefMode(enhancePayload) {
863
+ const fulfillmentRefMode = this.getFulfillmentRefMode();
864
+ if (!fulfillmentRefMode) return enhancePayload;
865
+ return (payload, ctx) => {
866
+ const enhancedPayload = enhancePayload ? enhancePayload(payload, ctx) : payload;
867
+ return {
868
+ ...enhancedPayload,
869
+ [OS_FULFILLMENT_REF_MODE_FIELD]: fulfillmentRefMode
870
+ };
871
+ };
872
+ }
848
873
 
849
874
  /**
850
875
  * 工厂入口:根据子模块名实例化对应模块。
@@ -1822,6 +1847,7 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
1822
1847
  // this.store.order.persistTempOrder();
1823
1848
  const inferredPaymentStatus = this.getSubmitPaymentStatus(params?.paymentStatus);
1824
1849
  const smallTicketDataFlag = params?.smallTicketDataFlag ?? this.getDefaultCheckoutSmallTicketDataFlag();
1850
+ const enhancePayload = this.buildSubmitPayloadEnhancerWithFulfillmentRefMode(params?.enhancePayload);
1825
1851
  const submitParams = {
1826
1852
  cacheId: this.cacheId,
1827
1853
  platform: this.otherParams?.platform,
@@ -1841,8 +1867,8 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
1841
1867
  ...(params?.confirmPendingVoucherPayments !== undefined ? {
1842
1868
  confirmPendingVoucherPayments: params.confirmPendingVoucherPayments
1843
1869
  } : {}),
1844
- ...(params?.enhancePayload !== undefined ? {
1845
- enhancePayload: params.enhancePayload
1870
+ ...(enhancePayload !== undefined ? {
1871
+ enhancePayload
1846
1872
  } : {})
1847
1873
  };
1848
1874
  return this.shouldUseCheckoutSyncTask() ? this.store.order.submitTempOrder(submitParams) : this.store.order.submitTempOrderAsync(submitParams);
@@ -1868,6 +1894,7 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
1868
1894
  }
1869
1895
  const inferredPaymentStatus = this.getSubmitPaymentStatus(params?.paymentStatus);
1870
1896
  const smallTicketDataFlag = params?.smallTicketDataFlag ?? this.getDefaultCheckoutSmallTicketDataFlag();
1897
+ const enhancePayload = this.buildSubmitPayloadEnhancerWithFulfillmentRefMode(params?.enhancePayload);
1871
1898
  const submitParams = {
1872
1899
  cacheId: this.cacheId,
1873
1900
  platform: this.otherParams?.platform,
@@ -1887,8 +1914,8 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
1887
1914
  ...(params?.confirmPendingVoucherPayments !== undefined ? {
1888
1915
  confirmPendingVoucherPayments: params.confirmPendingVoucherPayments
1889
1916
  } : {}),
1890
- ...(params?.enhancePayload !== undefined ? {
1891
- enhancePayload: params.enhancePayload
1917
+ ...(enhancePayload !== undefined ? {
1918
+ enhancePayload
1892
1919
  } : {})
1893
1920
  };
1894
1921
  return this.store.order.submitTempOrderAsync(submitParams);
@@ -1961,6 +1988,7 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
1961
1988
  if (!this.store.order) throw new Error('order 模块未初始化');
1962
1989
  const businessCode = params.businessCode ?? this.otherParams?.businessCode ?? this.otherParams?.business_code;
1963
1990
  const channel = params.channel ?? this.getSubmitOrderSalesChannel();
1991
+ const enhancePayload = this.buildSubmitPayloadEnhancerWithFulfillmentRefMode(params.enhancePayload);
1964
1992
  const syncParams = {
1965
1993
  ...params,
1966
1994
  checkoutSyncTaskEnabled: this.shouldUseCheckoutSyncTask(),
@@ -1969,6 +1997,9 @@ class BaseSalesImpl extends _BaseModule.BaseModule {
1969
1997
  } : {}),
1970
1998
  ...(channel !== undefined ? {
1971
1999
  channel
2000
+ } : {}),
2001
+ ...(enhancePayload !== undefined ? {
2002
+ enhancePayload
1972
2003
  } : {})
1973
2004
  };
1974
2005
  const syncState = this.store.order.getOrderSyncState();
@@ -326,7 +326,7 @@ export declare class BookingByStepImpl extends BaseModule implements Module {
326
326
  date: string;
327
327
  status: string;
328
328
  week: string;
329
- weekNum: 0 | 2 | 1 | 5 | 4 | 3 | 6;
329
+ weekNum: 0 | 1 | 5 | 2 | 4 | 3 | 6;
330
330
  }[]>;
331
331
  submitTimeSlot(timeSlots: TimeSliceItem): void;
332
332
  private getScheduleDataByIds;
@@ -345,7 +345,7 @@ export declare class BookingByStepImpl extends BaseModule implements Module {
345
345
  count: number;
346
346
  left: number;
347
347
  summaryCount: number;
348
- status: "lots_of_space" | "filling_up_fast" | "sold_out";
348
+ status: "sold_out" | "lots_of_space" | "filling_up_fast";
349
349
  }[];
350
350
  /**
351
351
  * 找到多个资源的公共可用时间段
@@ -334,7 +334,7 @@ export declare class BookingTicketImpl extends BaseSalesImpl implements Module {
334
334
  * 获取当前的客户搜索条件
335
335
  * @returns 当前搜索条件
336
336
  */
337
- getCurrentCustomerSearchParams(): Omit<import("../../modules").ShopGetCustomerListParams, "skip" | "num">;
337
+ getCurrentCustomerSearchParams(): Omit<import("../../modules").ShopGetCustomerListParams, "num" | "skip">;
338
338
  /**
339
339
  * 获取客户列表状态(包含滚动加载相关状态)
340
340
  * @returns 客户状态
@@ -453,7 +453,19 @@ export declare class BookingTicketImpl extends BaseSalesImpl implements Module {
453
453
  * 加车两阶段主决策(规格弹窗 / 资源编辑 / 直接加车)。
454
454
  * 与 ticketBooking `handleSelectProduct` + `handleBooking4Service` 等价。
455
455
  */
456
- decideAddProduct(item: any, options?: Partial<AddProductDecideContext>): import("./utils/addProductDecision").AddProductDecision;
456
+ decideAddProduct(item: any, options?: Partial<AddProductDecideContext>): {
457
+ action: "reloadCatalog";
458
+ } | {
459
+ action: "add";
460
+ cacheItem: any;
461
+ } | {
462
+ action: "requiresDetail";
463
+ payload: AddProductRequiresDetailPayload;
464
+ } | {
465
+ action: "requiresBookingEdit";
466
+ cacheItem: any;
467
+ payload: import("./utils/addProductDecision").AddProductRequiresBookingEditPayload;
468
+ };
457
469
  /** 规格弹窗 callback 后的第二段决策。 */
458
470
  decideAfterDetail(cacheItem: any, options?: Partial<AddProductDecideContext>): import("./utils/addProductDecision").AddProductDecision;
459
471
  /**
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "private": false,
3
3
  "name": "@pisell/pisellos",
4
- "version": "2.2.285",
4
+ "version": "2.2.286",
5
5
  "description": "一个可扩展的前端模块化SDK框架,支持插件系统",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",