@pisell/pisellos 2.1.168 → 2.1.170

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 (75) hide show
  1. package/dist/index.d.ts +1 -0
  2. package/dist/index.js +3 -0
  3. package/dist/model/strategy/adapter/dataVariant/adapter.d.ts +50 -0
  4. package/dist/model/strategy/adapter/dataVariant/adapter.js +167 -0
  5. package/dist/model/strategy/adapter/dataVariant/evaluator.d.ts +89 -0
  6. package/dist/model/strategy/adapter/dataVariant/evaluator.js +867 -0
  7. package/dist/model/strategy/adapter/dataVariant/examples.d.ts +39 -0
  8. package/dist/model/strategy/adapter/dataVariant/examples.js +277 -0
  9. package/dist/model/strategy/adapter/dataVariant/index.d.ts +4 -0
  10. package/dist/model/strategy/adapter/dataVariant/index.js +4 -0
  11. package/dist/model/strategy/adapter/dataVariant/type.d.ts +149 -0
  12. package/dist/model/strategy/adapter/dataVariant/type.js +54 -0
  13. package/dist/model/strategy/adapter/index.d.ts +4 -0
  14. package/dist/model/strategy/adapter/index.js +5 -1
  15. package/dist/modules/Discount/index.d.ts +5 -2
  16. package/dist/modules/Discount/index.js +21 -1
  17. package/dist/modules/Discount/types.d.ts +4 -0
  18. package/dist/modules/Order/index.d.ts +1 -0
  19. package/dist/modules/Order/index.js +18 -6
  20. package/dist/modules/Order/types.d.ts +2 -0
  21. package/dist/modules/ProductList/index.d.ts +3 -1
  22. package/dist/modules/ProductList/index.js +22 -11
  23. package/dist/modules/Schedule/getDateIsInSchedule.js +5 -0
  24. package/dist/solution/ScanOrder/index.js +1 -1
  25. package/dist/solution/ShopDiscount/index.js +16 -15
  26. package/dist/solution/VenueBooking/index.d.ts +49 -3
  27. package/dist/solution/VenueBooking/index.js +537 -287
  28. package/dist/solution/VenueBooking/types.d.ts +3 -1
  29. package/dist/solution/VenueBooking/types.js +1 -0
  30. package/dist/solution/VenueBooking/utils/dateSummary.d.ts +2 -2
  31. package/dist/solution/VenueBooking/utils/dateSummary.js +12 -25
  32. package/dist/solution/VenueBooking/utils/slotMerge.d.ts +0 -6
  33. package/dist/solution/VenueBooking/utils/slotMerge.js +3 -9
  34. package/dist/solution/VenueBooking/utils/smartPricing.d.ts +71 -0
  35. package/dist/solution/VenueBooking/utils/smartPricing.js +302 -0
  36. package/dist/solution/VenueBooking/utils/timeSlot.d.ts +2 -0
  37. package/dist/solution/VenueBooking/utils/timeSlot.js +9 -4
  38. package/lib/index.d.ts +1 -0
  39. package/lib/index.js +3 -0
  40. package/lib/model/strategy/adapter/dataVariant/adapter.d.ts +50 -0
  41. package/lib/model/strategy/adapter/dataVariant/adapter.js +149 -0
  42. package/lib/model/strategy/adapter/dataVariant/evaluator.d.ts +89 -0
  43. package/lib/model/strategy/adapter/dataVariant/evaluator.js +666 -0
  44. package/lib/model/strategy/adapter/dataVariant/examples.d.ts +39 -0
  45. package/lib/model/strategy/adapter/dataVariant/examples.js +293 -0
  46. package/lib/model/strategy/adapter/dataVariant/index.d.ts +4 -0
  47. package/lib/model/strategy/adapter/dataVariant/index.js +38 -0
  48. package/lib/model/strategy/adapter/dataVariant/type.d.ts +149 -0
  49. package/lib/model/strategy/adapter/dataVariant/type.js +31 -0
  50. package/lib/model/strategy/adapter/index.d.ts +4 -0
  51. package/lib/model/strategy/adapter/index.js +13 -2
  52. package/lib/modules/Discount/index.d.ts +5 -2
  53. package/lib/modules/Discount/index.js +11 -1
  54. package/lib/modules/Discount/types.d.ts +4 -0
  55. package/lib/modules/Order/index.d.ts +1 -0
  56. package/lib/modules/Order/index.js +11 -1
  57. package/lib/modules/Order/types.d.ts +2 -0
  58. package/lib/modules/ProductList/index.d.ts +3 -1
  59. package/lib/modules/ProductList/index.js +45 -39
  60. package/lib/modules/Schedule/getDateIsInSchedule.js +4 -0
  61. package/lib/solution/ScanOrder/index.js +1 -1
  62. package/lib/solution/ShopDiscount/index.js +3 -2
  63. package/lib/solution/VenueBooking/index.d.ts +49 -3
  64. package/lib/solution/VenueBooking/index.js +362 -113
  65. package/lib/solution/VenueBooking/types.d.ts +3 -1
  66. package/lib/solution/VenueBooking/types.js +1 -0
  67. package/lib/solution/VenueBooking/utils/dateSummary.d.ts +2 -2
  68. package/lib/solution/VenueBooking/utils/dateSummary.js +5 -10
  69. package/lib/solution/VenueBooking/utils/slotMerge.d.ts +0 -6
  70. package/lib/solution/VenueBooking/utils/slotMerge.js +3 -4
  71. package/lib/solution/VenueBooking/utils/smartPricing.d.ts +71 -0
  72. package/lib/solution/VenueBooking/utils/smartPricing.js +225 -0
  73. package/lib/solution/VenueBooking/utils/timeSlot.d.ts +2 -0
  74. package/lib/solution/VenueBooking/utils/timeSlot.js +7 -3
  75. package/package.json +1 -1
@@ -50,6 +50,7 @@ var import_resource = require("./utils/resource");
50
50
  var import_timeSlot = require("./utils/timeSlot");
51
51
  var import_dateSummary = require("./utils/dateSummary");
52
52
  var import_slotMerge = require("./utils/slotMerge");
53
+ var import_smartPricing = require("./utils/smartPricing");
53
54
  var import_utils3 = require("../../modules/Order/utils");
54
55
  var import_Order = require("../../modules/Order");
55
56
  var import_types4 = require("../RegisterAndLogin/types");
@@ -110,6 +111,10 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
110
111
  this.loadAllProductsInFlight = null;
111
112
  this.productsLoaded = false;
112
113
  this.loadOpenDataConfigInFlight = null;
114
+ /** 按日期缓存 slot 智能定价 Map,避免同周重复 resolveProducts */
115
+ this.slotPriceMapCache = /* @__PURE__ */ new Map();
116
+ /** loadProducts 后缓存的全量商品(venue + addon),供分段算价复用 */
117
+ this.smartPricingProductCatalog = [];
113
118
  }
114
119
  getLoggerContext() {
115
120
  return {
@@ -185,6 +190,36 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
185
190
  extra
186
191
  });
187
192
  }
193
+ /**
194
+ * 临时线上排查日志:
195
+ * - 仅对指定页面 cacheId 开启,避免影响其他页面
196
+ * - 不 await,且吞掉日志上报异常,保证日志系统不会阻断登录/商品加载主流程
197
+ */
198
+ logPricingDiagnostics(title, payload = {}) {
199
+ if (!this.isPricingDiagnosticsEnabled())
200
+ return;
201
+ void this.addVenueBookingLog({
202
+ level: "info",
203
+ title,
204
+ payload
205
+ }).catch((error) => {
206
+ console.warn("[VenueBooking] pricing diagnostic log failed", error);
207
+ });
208
+ }
209
+ isPricingDiagnosticsEnabled() {
210
+ return String(this.cacheId || "") === _VenueBookingImpl.PRICING_DEBUG_CACHE_ID;
211
+ }
212
+ getAvailableWalletIdsForDiagnostics() {
213
+ var _a, _b;
214
+ try {
215
+ return (_b = (_a = this.store.order) == null ? void 0 : _a.getAvailableWalletIds) == null ? void 0 : _b.call(_a);
216
+ } catch (error) {
217
+ return {
218
+ readFailed: true,
219
+ error: this.serializeError(error)
220
+ };
221
+ }
222
+ }
188
223
  logMethodStart(method, payload = {}) {
189
224
  void this.addVenueBookingLog({
190
225
  level: "info",
@@ -272,25 +307,45 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
272
307
  });
273
308
  }
274
309
  registerCustomerLoginListeners() {
275
- var _a, _b, _c, _d;
276
310
  this.clearLoginEffectListeners();
277
- const accountModule = (_a = this.core) == null ? void 0 : _a.getModule("account");
278
- const registerAndLoginModule = (_b = this.core) == null ? void 0 : _b.getModule("registerAndLogin");
279
- const currentAccount = ((_c = accountModule == null ? void 0 : accountModule.getCurrentAccount) == null ? void 0 : _c.call(accountModule)) || ((_d = accountModule == null ? void 0 : accountModule.getAccount) == null ? void 0 : _d.call(accountModule)) || null;
280
- const createHandleLogin = (eventName) => async (payload) => {
311
+ const createHandleLogin = () => async (payload) => {
281
312
  const customerId = this.resolveCustomerIdFromLoginPayload(payload);
282
- if (!customerId)
313
+ this.logPricingDiagnostics("[VenueBooking] customer login event received", {
314
+ customerId,
315
+ hasLoadAllProductsInFlight: !!this.loadAllProductsInFlight,
316
+ productsLoaded: this.productsLoaded,
317
+ smartPricingProductCatalogCount: this.smartPricingProductCatalog.length
318
+ });
319
+ if (!customerId) {
320
+ this.logPricingDiagnostics(
321
+ "[VenueBooking] customer login event skipped customerId not found in login payload"
322
+ );
283
323
  return;
324
+ }
284
325
  await this.refreshOrderMarketingAfterLogin({ customerId });
285
326
  };
286
- this.registerLoginEffect(import_types3.AccountHooks.OnLogin, createHandleLogin(import_types3.AccountHooks.OnLogin));
287
- this.registerLoginEffect(import_types4.RegisterAndLoginHooks.onLoginSuccess, createHandleLogin(import_types4.RegisterAndLoginHooks.onLoginSuccess));
327
+ this.registerLoginEffect(_VenueBookingImpl.PISELL1_LOGIN_SUCCESS, createHandleLogin());
328
+ this.registerLoginEffect(import_types3.AccountHooks.OnLogin, createHandleLogin());
329
+ this.registerLoginEffect(import_types4.RegisterAndLoginHooks.onLoginSuccess, createHandleLogin());
288
330
  }
289
331
  async refreshOrderMarketingAfterLogin(params) {
290
332
  if (!this.store.order)
291
333
  throw new Error("order 模块未初始化");
334
+ this.logPricingDiagnostics("[VenueBooking] refreshOrderMarketingAfterLogin entered", {
335
+ customerId: params.customerId,
336
+ hasCustomerLoginRefreshInFlight: !!this.customerLoginRefreshInFlight,
337
+ customerLoginRefreshIdInFlight: this.customerLoginRefreshIdInFlight,
338
+ hasLoadAllProductsInFlight: !!this.loadAllProductsInFlight,
339
+ productsLoaded: this.productsLoaded,
340
+ smartPricingProductCatalogCount: this.smartPricingProductCatalog.length,
341
+ note: "This login refresh recalculates the loaded catalog and does not call loadAllProducts"
342
+ });
292
343
  if (this.customerLoginRefreshInFlight) {
293
344
  if (this.customerLoginRefreshIdInFlight === params.customerId) {
345
+ this.logPricingDiagnostics("[VenueBooking] customer login refresh reused", {
346
+ customerId: params.customerId,
347
+ reason: "same customer refresh is already in flight"
348
+ });
294
349
  await this.customerLoginRefreshInFlight;
295
350
  return;
296
351
  }
@@ -298,20 +353,24 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
298
353
  }
299
354
  this.customerLoginRefreshIdInFlight = params.customerId;
300
355
  const refreshTask = (async () => {
301
- var _a;
302
- if (this.store.quotation) {
303
- await this.store.quotation.loadQuotations({
304
- channel: (_a = this.otherParams) == null ? void 0 : _a.channel
305
- });
306
- }
307
- this.recalculateOrderPricesFromQuotation();
356
+ var _a, _b, _c;
357
+ this.smartPricingCustomerId = params.customerId;
308
358
  await this.store.order.loadDiscountConfig({
309
359
  customerId: params.customerId
310
360
  });
361
+ this.clearSlotPriceMapCache();
362
+ this.recalculateOrderPricesFromSmartPricing();
363
+ this.store.order.applyDiscount();
364
+ this.normalizeDiscountedOrderPrices(
365
+ this.store.order.getDiscountList()
366
+ );
311
367
  await this.store.order.recalculateSummary({ createIfMissing: true });
312
368
  this.store.order.persistTempOrder();
313
369
  await this.refreshItemRuleQuantityLimits();
314
370
  await this.refreshCartValidationPassed();
371
+ await ((_c = (_b = (_a = this.core) == null ? void 0 : _a.effects) == null ? void 0 : _b.emit) == null ? void 0 : _c.call(_b, import_types.VenueBookingHooks.onSmartPricingUpdated, {
372
+ customerId: params.customerId
373
+ }));
315
374
  })();
316
375
  this.customerLoginRefreshInFlight = refreshTask;
317
376
  try {
@@ -326,11 +385,12 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
326
385
  }
327
386
  }
328
387
  async initialize(core, options = {}) {
329
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
388
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i;
330
389
  this.logMethodStart("initialize");
331
390
  this.core = core;
332
391
  this.initializeOptions = options || {};
333
392
  this.store = { ...this.store, ...options.store };
393
+ this.smartPricingCustomerId = void 0;
334
394
  this.store.entryContext = ((_a = options.otherParams) == null ? void 0 : _a.entryContext) || this.store.entryContext;
335
395
  this.store.status = "initializing";
336
396
  this.store.error = null;
@@ -342,6 +402,12 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
342
402
  this.store.slotConfig = { ...this.baseSlotConfig };
343
403
  this.otherParams = options.otherParams || {};
344
404
  this.cacheId = (_c = this.otherParams) == null ? void 0 : _c.cacheId;
405
+ this.logPricingDiagnostics("[VenueBooking] initialize product loading state", {
406
+ hasLoadAllProductsInFlight: !!this.loadAllProductsInFlight,
407
+ productsLoaded: this.productsLoaded,
408
+ smartPricingProductCatalogCount: this.smartPricingProductCatalog.length,
409
+ note: "initialize currently does not reset the product loading state"
410
+ });
345
411
  this.itemRuleRuntimeConfig = ((_d = this.otherParams) == null ? void 0 : _d.venueBookingItemRule) || ((_e = this.otherParams) == null ? void 0 : _e.itemRule) || {};
346
412
  this.itemRuleConfigs = [];
347
413
  this.itemRuleConfigsPromise = null;
@@ -462,10 +528,6 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
462
528
  await this.loadRuntimeConfigs();
463
529
  if (this.store.schedule) {
464
530
  await this.store.schedule.loadAllSchedule();
465
- this.injectScheduleResolverToQuotation();
466
- (_k = this.store.quotation) == null ? void 0 : _k.loadQuotations({
467
- channel: (_j = this.otherParams) == null ? void 0 : _j.channel
468
- });
469
531
  }
470
532
  await this.refreshItemRuleQuantityLimits();
471
533
  this.store.status = "ready";
@@ -541,11 +603,124 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
541
603
  getCartValidation() {
542
604
  return this.store.cartValidation || { passed: null, failures: [] };
543
605
  }
606
+ /** 清除 slot 价格分段缓存(商品重载 / 登录后 customerId 变化时调用) */
607
+ clearSlotPriceMapCache() {
608
+ this.slotPriceMapCache.clear();
609
+ }
610
+ /** 读取当前登录 customerId */
611
+ resolveCustomerIdForSmartPricing() {
612
+ var _a, _b, _c;
613
+ if (this.smartPricingCustomerId)
614
+ return this.smartPricingCustomerId;
615
+ try {
616
+ const userPlugin = (_a = this.core) == null ? void 0 : _a.getPlugin("user");
617
+ const customerId = Number((_c = (_b = userPlugin == null ? void 0 : userPlugin.get) == null ? void 0 : _b.call(userPlugin)) == null ? void 0 : _c.id);
618
+ return Number.isFinite(customerId) && customerId > 0 ? customerId : void 0;
619
+ } catch {
620
+ return void 0;
621
+ }
622
+ }
623
+ resolveAvailableWalletIdsForSmartPricing() {
624
+ var _a, _b, _c, _d, _e, _f, _g, _h;
625
+ const orderWalletIds = (_b = (_a = this.store.order) == null ? void 0 : _a.getAvailableWalletIds) == null ? void 0 : _b.call(_a);
626
+ const fallbackWalletIds = ((_c = this.otherParams) == null ? void 0 : _c.available_wallet_ids) || ((_d = this.otherParams) == null ? void 0 : _d.availableWalletIds) || ((_f = (_e = this.otherParams) == null ? void 0 : _e.strategy_context) == null ? void 0 : _f.available_wallet_ids) || ((_h = (_g = this.otherParams) == null ? void 0 : _g.strategy_context) == null ? void 0 : _h.availableWalletIds);
627
+ const source = Array.isArray(orderWalletIds) && orderWalletIds.length ? orderWalletIds : fallbackWalletIds;
628
+ if (!Array.isArray(source))
629
+ return [];
630
+ const ids = source.map((item) => Number(item)).filter((item) => Number.isFinite(item) && item > 0);
631
+ return Array.from(new Set(ids));
632
+ }
633
+ /** 读取 schedule 全量列表,供智能定价 schedule_match 使用 */
634
+ getScheduleListForSmartPricing() {
635
+ var _a;
636
+ const scheduleModule = this.store.schedule;
637
+ const list = (_a = scheduleModule == null ? void 0 : scheduleModule.store) == null ? void 0 : _a.scheduleList;
638
+ return Array.isArray(list) ? list : [];
639
+ }
640
+ /** 构建智能定价上下文,对齐 OS Server buildProductFormatterContext */
641
+ buildSmartPricingContext(customerId) {
642
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
643
+ const evaluator = (_b = (_a = this.core) == null ? void 0 : _a.context) == null ? void 0 : _b.dataVariantEvaluator;
644
+ return {
645
+ evaluator,
646
+ scheduleList: this.getScheduleListForSmartPricing(),
647
+ menuList: ((_c = this.otherParams) == null ? void 0 : _c.menuList) || ((_e = (_d = this.otherParams) == null ? void 0 : _d.openData) == null ? void 0 : _e.menuList) || [],
648
+ customerId: customerId ?? this.resolveCustomerIdForSmartPricing(),
649
+ availableWalletIds: this.resolveAvailableWalletIdsForSmartPricing(),
650
+ channel: (_f = this.otherParams) == null ? void 0 : _f.channel,
651
+ businessCode: (_g = this.otherParams) == null ? void 0 : _g.businessCode,
652
+ orderType: ((_h = this.otherParams) == null ? void 0 : _h.orderType) || ((_i = this.otherParams) == null ? void 0 : _i.type),
653
+ strategy_context: (_j = this.otherParams) == null ? void 0 : _j.strategy_context,
654
+ ...this.isPricingDiagnosticsEnabled() ? {
655
+ debugLog: (title, payload = {}) => this.logPricingDiagnostics(title, payload)
656
+ } : {}
657
+ };
658
+ }
659
+ /** venue + addon 全量商品,供智能定价评估 */
660
+ getAllProductsForSmartPricing() {
661
+ if (this.smartPricingProductCatalog.length) {
662
+ return this.smartPricingProductCatalog;
663
+ }
664
+ const venueList = this.getVenueProducts();
665
+ const addonList = this.getAddonProductsList();
666
+ return [...venueList, ...addonList];
667
+ }
668
+ getSlotPriceMapCacheKey(date, context) {
669
+ return [
670
+ date,
671
+ context.customerId ?? "",
672
+ (context.availableWalletIds || []).join(","),
673
+ context.channel ?? "",
674
+ context.businessCode ?? ""
675
+ ].join("|");
676
+ }
677
+ /**
678
+ * 按 schedule 分段构建 slot 价格 Map,带实例级缓存。
679
+ */
680
+ buildSlotPriceMapForDate(date, config, productIds) {
681
+ const context = this.buildSmartPricingContext();
682
+ const cacheKey = this.getSlotPriceMapCacheKey(date, context);
683
+ const cached = this.slotPriceMapCache.get(cacheKey);
684
+ if (cached)
685
+ return cached;
686
+ const timeLabels = (0, import_timeSlot.generateTimeLabels)(config);
687
+ const slotPriceMap = (0, import_smartPricing.buildSlotPriceMapByScheduleSegments)({
688
+ products: this.getAllProductsForSmartPricing(),
689
+ productIds,
690
+ date,
691
+ timeLabels,
692
+ config,
693
+ context
694
+ });
695
+ this.slotPriceMapCache.set(cacheKey, slotPriceMap);
696
+ return slotPriceMap;
697
+ }
544
698
  // ─── 场地商品 & 附加商品 ───
545
699
  async loadAllProducts() {
546
- if (this.loadAllProductsInFlight)
700
+ this.logPricingDiagnostics("[VenueBooking] loadAllProducts called", {
701
+ hasLoadAllProductsInFlight: !!this.loadAllProductsInFlight,
702
+ productsLoaded: this.productsLoaded,
703
+ smartPricingCustomerId: this.smartPricingCustomerId,
704
+ smartPricingProductCatalogCount: this.smartPricingProductCatalog.length,
705
+ venueProducts: !this.store.venueProducts,
706
+ addonProducts: !this.store.addonProducts
707
+ });
708
+ if (this.loadAllProductsInFlight) {
709
+ this.logPricingDiagnostics("[VenueBooking] loadAllProducts request skipped", {
710
+ reason: "reusing existing loadAllProductsInFlight promise",
711
+ productsLoaded: this.productsLoaded
712
+ });
547
713
  return this.loadAllProductsInFlight;
714
+ }
715
+ this.logPricingDiagnostics("[VenueBooking] loadAllProducts request starting", {
716
+ productsLoaded: this.productsLoaded,
717
+ smartPricingCustomerId: this.smartPricingCustomerId
718
+ });
548
719
  this.loadAllProductsInFlight = this._doLoadAllProducts().finally(() => {
720
+ this.logPricingDiagnostics("[VenueBooking] loadAllProducts request settled", {
721
+ productsLoaded: this.productsLoaded,
722
+ smartPricingProductCatalogCount: this.smartPricingProductCatalog.length
723
+ });
549
724
  this.loadAllProductsInFlight = null;
550
725
  });
551
726
  return this.loadAllProductsInFlight;
@@ -569,9 +744,28 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
569
744
  menu_list_ids: menuListIds,
570
745
  cacheId: this.cacheId,
571
746
  schedule_date: (0, import_dayjs.default)().format("YYYY-MM-DD"),
572
- schedule_datetime: (0, import_dayjs.default)().format("YYYY-MM-DD HH:mm:ss")
747
+ schedule_datetime: (0, import_dayjs.default)().format("YYYY-MM-DD HH:mm:ss"),
748
+ with_extra: ["dataVariants"]
573
749
  });
574
- const list = Array.isArray(allProducts) ? allProducts : [];
750
+ const rawList = Array.isArray(allProducts) ? allProducts : [];
751
+ let list = rawList;
752
+ this.smartPricingProductCatalog = rawList.slice();
753
+ const smartPricingContext = this.buildSmartPricingContext();
754
+ list = (0, import_smartPricing.formatProductsWithDataVariant)(
755
+ list,
756
+ smartPricingContext,
757
+ (0, import_dayjs.default)().format("YYYY-MM-DD HH:mm:ss")
758
+ );
759
+ if (this.isPricingDiagnosticsEnabled()) {
760
+ this.logPricingDiagnostics("[VenueBooking] loadAllProducts smart pricing context", {
761
+ smartPricingContext,
762
+ rawList,
763
+ list,
764
+ otherParams: this.otherParams,
765
+ availableWalletIds: this.getAvailableWalletIdsForDiagnostics()
766
+ });
767
+ }
768
+ this.clearSlotPriceMapCache();
575
769
  const venueList = list.filter((p) => p.duration != null);
576
770
  const addonList = list.filter((p) => p.duration == null);
577
771
  const venueStore = (_c = this.store.venueProducts) == null ? void 0 : _c.store;
@@ -594,6 +788,10 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
594
788
  }
595
789
  }
596
790
  async loadVenueProducts() {
791
+ this.logPricingDiagnostics("[VenueBooking] loadVenueProducts called", {
792
+ hasLoadAllProductsInFlight: !!this.loadAllProductsInFlight,
793
+ productsLoaded: this.productsLoaded
794
+ });
597
795
  const result = await this.loadAllProducts();
598
796
  return result.venueProducts;
599
797
  }
@@ -752,29 +950,24 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
752
950
  config: this.baseSlotConfig,
753
951
  rawResources: this.store.rawResourceData,
754
952
  resourceProductMap: this.resourceProductMap,
755
- quotationModule: this.store.quotation,
756
- resolveConfig: (date) => this.resolveSlotConfigForDate(date)
953
+ resolveConfig: (date) => this.resolveSlotConfigForDate(date),
954
+ buildSlotPriceMap: (date, config, productIds) => this.buildSlotPriceMapForDate(date, config, productIds)
757
955
  });
758
956
  }
759
957
  getTimeSlotGrid(date) {
760
958
  const resolvedSlotConfig = this.syncOperatingHoursToSlotConfig(date);
761
- let quotationPriceMap;
762
- if (this.store.quotation) {
763
- const productIds = [
764
- ...new Set(
765
- [...this.resourceProductMap.values()].flat().map((m) => m.productId)
766
- )
767
- ];
768
- const timeLabels = (0, import_timeSlot.generateTimeLabels)(resolvedSlotConfig);
769
- const timePoints = timeLabels.map((label) => `${date} ${label}`);
770
- quotationPriceMap = this.store.quotation.buildProductPriceMap({ productIds, timePoints });
771
- }
959
+ const productIds = [
960
+ ...new Set(
961
+ [...this.resourceProductMap.values()].flat().map((m) => m.productId)
962
+ )
963
+ ];
964
+ const slotPriceMap = productIds.length ? this.buildSlotPriceMapForDate(date, resolvedSlotConfig, productIds) : void 0;
772
965
  return (0, import_timeSlot.buildTimeSlotGrid)({
773
966
  date,
774
967
  config: resolvedSlotConfig,
775
968
  rawResources: this.store.rawResourceData,
776
969
  resourceProductMap: this.resourceProductMap,
777
- quotationPriceMap
970
+ slotPriceMap
778
971
  });
779
972
  }
780
973
  // ─── 时间槽订单操作 ───
@@ -1059,8 +1252,7 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
1059
1252
  booking_uid: bookingUuid,
1060
1253
  price_breakdown: (0, import_slotMerge.buildPriceBreakdown)({
1061
1254
  group,
1062
- productId: mapping.productId,
1063
- quotation: this.store.quotation
1255
+ productId: mapping.productId
1064
1256
  })
1065
1257
  },
1066
1258
  _origin: {
@@ -1153,20 +1345,12 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
1153
1345
  if (!this.store.schedule)
1154
1346
  throw new Error("schedule 模块未初始化");
1155
1347
  await this.store.schedule.loadAllSchedule();
1156
- this.injectScheduleResolverToQuotation();
1157
1348
  this.logMethodSuccess("loadSchedules");
1158
1349
  } catch (error) {
1159
1350
  this.logMethodError("loadSchedules", error);
1160
1351
  throw error;
1161
1352
  }
1162
1353
  }
1163
- injectScheduleResolverToQuotation() {
1164
- if (this.store.quotation && this.store.schedule) {
1165
- this.store.quotation.setScheduleResolver(
1166
- (id) => this.store.schedule.getScheduleListByIds([id])[0]
1167
- );
1168
- }
1169
- }
1170
1354
  getScheduleListByIds(ids) {
1171
1355
  if (!this.store.schedule)
1172
1356
  return [];
@@ -1205,14 +1389,16 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
1205
1389
  throw error;
1206
1390
  }
1207
1391
  }
1208
- recalculateOrderPricesFromQuotation() {
1392
+ recalculateOrderPricesFromSmartPricing() {
1209
1393
  var _a, _b;
1210
- if (!this.store.order || !this.store.quotation)
1394
+ if (!this.store.order)
1211
1395
  return;
1212
1396
  const tempOrder = this.store.order.getTempOrder();
1213
1397
  if (!((_a = tempOrder == null ? void 0 : tempOrder.products) == null ? void 0 : _a.length))
1214
1398
  return;
1215
1399
  const now = (0, import_dayjs.default)().format("YYYY-MM-DD HH:mm:ss");
1400
+ const catalog = this.getAllProductsForSmartPricing();
1401
+ const context = this.buildSmartPricingContext();
1216
1402
  for (const product of tempOrder.products) {
1217
1403
  if ((_b = product.metadata) == null ? void 0 : _b.venue_booking) {
1218
1404
  const mappings = this.resourceProductMap.get(product.metadata.resource_id);
@@ -1225,37 +1411,75 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
1225
1411
  product,
1226
1412
  this.store.slotConfig.slotDurationMinutes
1227
1413
  );
1228
- const updatedSlots = slots.map((slot) => ({
1229
- ...slot,
1230
- productId: mapping.productId,
1231
- price: this.store.quotation.getPriceForProduct({
1414
+ const uniqueDates = [
1415
+ ...new Set(slots.map((slot) => (0, import_dayjs.default)(slot.startTime).format("YYYY-MM-DD")))
1416
+ ];
1417
+ const slotPriceMaps = /* @__PURE__ */ new Map();
1418
+ for (const slotDate of uniqueDates) {
1419
+ slotPriceMaps.set(
1420
+ slotDate,
1421
+ this.buildSlotPriceMapForDate(slotDate, this.store.slotConfig, [mapping.productId])
1422
+ );
1423
+ }
1424
+ const updatedSlots = slots.map((slot) => {
1425
+ const slotDate = (0, import_dayjs.default)(slot.startTime).format("YYYY-MM-DD");
1426
+ const slotPriceMap = slotPriceMaps.get(slotDate);
1427
+ return {
1428
+ ...slot,
1232
1429
  productId: mapping.productId,
1233
- datetime: slot.startTime
1234
- }) ?? mapping.price
1235
- }));
1430
+ price: slotPriceMap ? (0, import_smartPricing.getSlotPriceFromMap)({
1431
+ slotPriceMap,
1432
+ productId: mapping.productId,
1433
+ slotStartTime: slot.startTime,
1434
+ fallbackPrice: mapping.price
1435
+ }) : mapping.price
1436
+ };
1437
+ });
1236
1438
  const merged = (0, import_slotMerge.mergeConsecutiveSlots)(updatedSlots);
1237
1439
  if (merged.length === 1) {
1238
- product.selling_price = merged[0].totalPrice;
1239
- product.original_price = merged[0].totalPrice;
1240
- product.metadata.price_breakdown = (0, import_slotMerge.buildPriceBreakdown)({
1241
- group: merged[0],
1242
- productId: mapping.productId,
1243
- quotation: this.store.quotation
1440
+ this.resetProductPriceForSmartPricing({
1441
+ product,
1442
+ price: merged[0].totalPrice,
1443
+ priceBreakdown: (0, import_slotMerge.buildPriceBreakdown)({
1444
+ group: merged[0],
1445
+ productId: mapping.productId
1446
+ })
1244
1447
  });
1245
1448
  }
1246
1449
  } else if (product.product_id != null) {
1247
- const quotationPrice = this.store.quotation.getPriceForProduct({
1248
- productId: product.product_id,
1249
- variantId: product.product_variant_id ?? void 0,
1250
- datetime: now
1450
+ const smartPrice = (0, import_smartPricing.getPriceForProductAtDatetime)({
1451
+ products: catalog,
1452
+ context,
1453
+ productId: Number(product.product_id),
1454
+ datetime: now,
1455
+ fallbackPrice: product.selling_price
1456
+ });
1457
+ this.resetProductPriceForSmartPricing({
1458
+ product,
1459
+ price: smartPrice
1251
1460
  });
1252
- if (quotationPrice !== null) {
1253
- product.selling_price = quotationPrice;
1254
- product.original_price = quotationPrice;
1255
- }
1256
1461
  }
1257
1462
  }
1258
1463
  }
1464
+ /**
1465
+ * DataVariant 重算价格后,同步刷新订单展示、Rules 与 Summary 共用的价格口径。
1466
+ * Rules 会在本轮结束后根据当前客户的 discountList 重新写入实际折扣。
1467
+ */
1468
+ resetProductPriceForSmartPricing(params) {
1469
+ const { product, price, priceBreakdown } = params;
1470
+ const metadata = product.metadata || {};
1471
+ product.selling_price = price;
1472
+ product.original_price = price;
1473
+ product.discount_list = [];
1474
+ product.metadata = {
1475
+ ...metadata,
1476
+ ...priceBreakdown ? { price_breakdown: priceBreakdown } : {},
1477
+ source_product_price: price,
1478
+ main_product_original_price: price,
1479
+ main_product_selling_price: price,
1480
+ price_schema_version: 2
1481
+ };
1482
+ }
1259
1483
  async scanCode(code, customerId) {
1260
1484
  this.logMethodStart("scanCode", { code });
1261
1485
  try {
@@ -1281,7 +1505,7 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
1281
1505
  }
1282
1506
  /** 勾选/取消勾选某张折扣券,重新计算折扣并持久化 */
1283
1507
  async setDiscountSelected(params) {
1284
- var _a, _b, _c, _d, _e, _f, _g, _h;
1508
+ var _a, _b, _c, _d, _e;
1285
1509
  this.logMethodStart("setDiscountSelected", params);
1286
1510
  try {
1287
1511
  if (!this.store.order)
@@ -1333,42 +1557,15 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
1333
1557
  }
1334
1558
  }
1335
1559
  }
1336
- const selectedResourceIds = new Set(
1337
- nextDiscountList.filter((d) => d.isSelected).map((d) => d.id)
1338
- );
1339
- for (const product of tempOrder.products) {
1340
- if ((_c = product._origin) == null ? void 0 : _c.isManualDiscount)
1341
- continue;
1342
- product.discount_list = (product.discount_list || []).filter((pd) => {
1343
- var _a2;
1344
- const rid = ((_a2 = pd.discount) == null ? void 0 : _a2.resource_id) ?? pd.id;
1345
- return rid != null && selectedResourceIds.has(rid);
1346
- });
1347
- const totalPerUnitDiscount = (product.discount_list || []).reduce(
1348
- (sum, pd) => sum + (pd.amount || 0),
1349
- 0
1350
- );
1351
- const optionSum = (0, import_utils3.sumOptionUnitPrice)(product.product_option_item);
1352
- const sourcePrice = ((_d = product.metadata) == null ? void 0 : _d.source_product_price) ?? (((_e = product.metadata) == null ? void 0 : _e.main_product_original_price) != null ? new import_decimal.default(Number(product.metadata.main_product_original_price) || 0).minus(optionSum).toFixed(2) : product.original_price ?? "0");
1353
- const newSourceSellingPrice = new import_decimal.default(Number(sourcePrice) || 0).minus(totalPerUnitDiscount).toDecimalPlaces(2).toString();
1354
- const newMainSellingPrice = new import_decimal.default(Number(newSourceSellingPrice) || 0).plus(optionSum).toDecimalPlaces(2).toFixed(2);
1355
- if (product.metadata) {
1356
- product.metadata.main_product_selling_price = newMainSellingPrice;
1357
- product.metadata.price_schema_version = 2;
1358
- }
1359
- product.selling_price = (0, import_utils3.composeLinePrice)({
1360
- mainPrice: newMainSellingPrice,
1361
- bundle: product.product_bundle
1362
- });
1363
- }
1560
+ this.normalizeDiscountedOrderPrices(nextDiscountList);
1364
1561
  import_Order.OrderModule.populateSavedAmounts(tempOrder.products, nextDiscountList);
1365
1562
  await (discountModule == null ? void 0 : discountModule.setDiscountList(nextDiscountList));
1366
1563
  tempOrder.discount_list = (nextDiscountList || []).filter((d) => d.isSelected);
1367
1564
  const afterApplyTarget = this.store.order.getDiscountList().find((d) => d.id === params.discountId) || null;
1368
1565
  await this.store.order.recalculateSummary({ createIfMissing: true });
1369
1566
  this.store.order.persistTempOrder();
1370
- const finalSummary = ((_f = this.store.order.getTempOrder()) == null ? void 0 : _f.summary) || null;
1371
- const finalProduct = ((_h = (_g = this.store.order.getTempOrder()) == null ? void 0 : _g.products) == null ? void 0 : _h[0]) || null;
1567
+ const finalSummary = ((_c = this.store.order.getTempOrder()) == null ? void 0 : _c.summary) || null;
1568
+ const finalProduct = ((_e = (_d = this.store.order.getTempOrder()) == null ? void 0 : _d.products) == null ? void 0 : _e[0]) || null;
1372
1569
  this.logMethodSuccess("setDiscountSelected", params);
1373
1570
  const finalTarget = this.store.order.getDiscountList().find((d) => d.id === params.discountId) || null;
1374
1571
  return this.store.order.getDiscountList();
@@ -1377,6 +1574,51 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
1377
1574
  throw error;
1378
1575
  }
1379
1576
  }
1577
+ /**
1578
+ * 统一 VenueBooking 折扣后的订单行价。
1579
+ *
1580
+ * Rules 的 `discount_list.amount` 是已经按适用规则计算出的每件优惠额;它是这里唯一
1581
+ * 的折扣金额来源。使用智能定价写入的 source_product_price 减去该金额,再同步
1582
+ * selling_price 与 Summary 使用的 main_product_selling_price,避免 UI 的购物车行价、
1583
+ * 小计和提交订单各自读取到不同的价格字段。
1584
+ */
1585
+ normalizeDiscountedOrderPrices(discountList) {
1586
+ var _a;
1587
+ const order = this.store.order;
1588
+ if (!order)
1589
+ return;
1590
+ const tempOrder = order.ensureTempOrder();
1591
+ const selectedResourceIds = new Set(
1592
+ (discountList || []).filter((discount) => discount.isSelected).map((discount) => discount.id)
1593
+ );
1594
+ for (const product of tempOrder.products) {
1595
+ if ((_a = product._origin) == null ? void 0 : _a.isManualDiscount)
1596
+ continue;
1597
+ product.discount_list = (product.discount_list || []).filter((detail) => {
1598
+ var _a2;
1599
+ const resourceId = ((_a2 = detail.discount) == null ? void 0 : _a2.resource_id) ?? detail.id;
1600
+ return resourceId != null && selectedResourceIds.has(resourceId);
1601
+ });
1602
+ const totalPerUnitDiscount = (product.discount_list || []).reduce(
1603
+ (total, detail) => total.plus(detail.amount || 0),
1604
+ new import_decimal.default(0)
1605
+ );
1606
+ const metadata = product.metadata || (product.metadata = {});
1607
+ const optionSum = (0, import_utils3.sumOptionUnitPrice)(product.product_option_item);
1608
+ const sourcePrice = metadata.source_product_price ?? (metadata.main_product_original_price != null ? new import_decimal.default(Number(metadata.main_product_original_price) || 0).minus(optionSum).toFixed(2) : product.original_price ?? "0");
1609
+ const nextSourceSellingPrice = import_decimal.default.max(
1610
+ 0,
1611
+ new import_decimal.default(Number(sourcePrice) || 0).minus(totalPerUnitDiscount)
1612
+ ).toDecimalPlaces(2).toString();
1613
+ const nextMainSellingPrice = new import_decimal.default(Number(nextSourceSellingPrice) || 0).plus(optionSum).toDecimalPlaces(2).toFixed(2);
1614
+ metadata.main_product_selling_price = nextMainSellingPrice;
1615
+ metadata.price_schema_version = 2;
1616
+ product.selling_price = (0, import_utils3.composeLinePrice)({
1617
+ mainPrice: nextMainSellingPrice,
1618
+ bundle: product.product_bundle
1619
+ });
1620
+ }
1621
+ }
1380
1622
  async addNewOrder() {
1381
1623
  this.logMethodStart("addNewOrder");
1382
1624
  try {
@@ -1456,7 +1698,7 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
1456
1698
  }
1457
1699
  }
1458
1700
  async addProductToOrder(product) {
1459
- var _a;
1701
+ var _a, _b;
1460
1702
  this.logMethodStart("addProductToOrder", {
1461
1703
  product_id: product.product_id,
1462
1704
  product_variant_id: product.product_variant_id
@@ -1464,18 +1706,18 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
1464
1706
  try {
1465
1707
  if (!this.store.order)
1466
1708
  throw new Error("order 模块未初始化");
1467
- if (!((_a = product.metadata) == null ? void 0 : _a.venue_booking) && this.store.quotation && product.product_id != null) {
1468
- const quotationPrice = this.store.quotation.getPriceForProduct({
1469
- productId: product.product_id,
1470
- variantId: product.product_variant_id ?? void 0,
1471
- datetime: (0, import_dayjs.default)().format("YYYY-MM-DD HH:mm:ss")
1709
+ if (!((_a = product.metadata) == null ? void 0 : _a.venue_booking) && product.product_id != null) {
1710
+ const smartPrice = (0, import_smartPricing.getPriceForProductAtDatetime)({
1711
+ products: this.getAllProductsForSmartPricing(),
1712
+ context: this.buildSmartPricingContext(),
1713
+ productId: Number(product.product_id),
1714
+ datetime: (0, import_dayjs.default)().format("YYYY-MM-DD HH:mm:ss"),
1715
+ fallbackPrice: product.selling_price
1472
1716
  });
1473
- if (quotationPrice !== null) {
1474
- product.selling_price = quotationPrice;
1475
- product.original_price = quotationPrice;
1476
- } else if (product.selling_price != null) {
1477
- product.original_price = product.selling_price;
1478
- }
1717
+ product.selling_price = smartPrice;
1718
+ product.original_price = smartPrice;
1719
+ } else if (product.selling_price != null && !((_b = product.metadata) == null ? void 0 : _b.venue_booking)) {
1720
+ product.original_price = product.selling_price;
1479
1721
  }
1480
1722
  const products = await this.store.order.addProductToOrder(product);
1481
1723
  await this.refreshItemRuleQuantityLimits();
@@ -1562,6 +1804,11 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
1562
1804
  }
1563
1805
  async getProductList() {
1564
1806
  if (this.productsLoaded) {
1807
+ this.logPricingDiagnostics("[VenueBooking] getProductList loadAllProducts skipped", {
1808
+ reason: "productsLoaded is true",
1809
+ addonProductCount: this.getAddonProductsList().length,
1810
+ smartPricingProductCatalogCount: this.smartPricingProductCatalog.length
1811
+ });
1565
1812
  return (0, import_utils.attachItemRuleLimitsToTopLevelProducts)(
1566
1813
  this.getAddonProductsList(),
1567
1814
  this.store.itemRuleQuantityLimits || []
@@ -1941,7 +2188,9 @@ var _VenueBookingImpl = class extends import_BaseModule.BaseModule {
1941
2188
  }
1942
2189
  };
1943
2190
  var VenueBookingImpl = _VenueBookingImpl;
2191
+ VenueBookingImpl.PRICING_DEBUG_CACHE_ID = "031596279011848516";
1944
2192
  VenueBookingImpl.OPEN_DATA_CACHE_TTL = 5 * 60 * 1e3;
2193
+ VenueBookingImpl.PISELL1_LOGIN_SUCCESS = "pisell1.login.success";
1945
2194
  // ─── UI 状态缓存(按 cacheId 分桶,sessionStorage) ───
1946
2195
  //
1947
2196
  // 用于物料层持久化 UI 层面的轻量状态(如当前步骤、登录回跳意图等)。