@pisell/pisellos 2.1.165 → 2.1.166

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 (78) 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 +780 -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 +148 -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/model/strategy/adapter/promotion/index.js +0 -9
  16. package/dist/modules/Discount/index.d.ts +5 -2
  17. package/dist/modules/Discount/index.js +21 -1
  18. package/dist/modules/Discount/types.d.ts +4 -0
  19. package/dist/modules/Order/index.d.ts +1 -0
  20. package/dist/modules/Order/index.js +18 -6
  21. package/dist/modules/Order/types.d.ts +2 -0
  22. package/dist/modules/ProductList/index.d.ts +3 -1
  23. package/dist/modules/ProductList/index.js +22 -11
  24. package/dist/modules/Schedule/getDateIsInSchedule.js +5 -0
  25. package/dist/solution/BookingByStep/index.d.ts +2 -2
  26. package/dist/solution/ScanOrder/index.js +1 -1
  27. package/dist/solution/ShopDiscount/index.js +16 -15
  28. package/dist/solution/VenueBooking/index.d.ts +40 -3
  29. package/dist/solution/VenueBooking/index.js +394 -261
  30. package/dist/solution/VenueBooking/types.d.ts +3 -1
  31. package/dist/solution/VenueBooking/types.js +1 -0
  32. package/dist/solution/VenueBooking/utils/dateSummary.d.ts +2 -2
  33. package/dist/solution/VenueBooking/utils/dateSummary.js +12 -25
  34. package/dist/solution/VenueBooking/utils/slotMerge.d.ts +0 -6
  35. package/dist/solution/VenueBooking/utils/slotMerge.js +3 -9
  36. package/dist/solution/VenueBooking/utils/smartPricing.d.ts +70 -0
  37. package/dist/solution/VenueBooking/utils/smartPricing.js +233 -0
  38. package/dist/solution/VenueBooking/utils/timeSlot.d.ts +2 -0
  39. package/dist/solution/VenueBooking/utils/timeSlot.js +9 -4
  40. package/lib/index.d.ts +1 -0
  41. package/lib/index.js +3 -0
  42. package/lib/model/strategy/adapter/dataVariant/adapter.d.ts +50 -0
  43. package/lib/model/strategy/adapter/dataVariant/adapter.js +149 -0
  44. package/lib/model/strategy/adapter/dataVariant/evaluator.d.ts +89 -0
  45. package/lib/model/strategy/adapter/dataVariant/evaluator.js +583 -0
  46. package/lib/model/strategy/adapter/dataVariant/examples.d.ts +39 -0
  47. package/lib/model/strategy/adapter/dataVariant/examples.js +293 -0
  48. package/lib/model/strategy/adapter/dataVariant/index.d.ts +4 -0
  49. package/lib/model/strategy/adapter/dataVariant/index.js +38 -0
  50. package/lib/model/strategy/adapter/dataVariant/type.d.ts +148 -0
  51. package/lib/model/strategy/adapter/dataVariant/type.js +31 -0
  52. package/lib/model/strategy/adapter/index.d.ts +4 -0
  53. package/lib/model/strategy/adapter/index.js +13 -2
  54. package/lib/modules/Discount/index.d.ts +5 -2
  55. package/lib/modules/Discount/index.js +11 -1
  56. package/lib/modules/Discount/types.d.ts +4 -0
  57. package/lib/modules/Order/index.d.ts +1 -0
  58. package/lib/modules/Order/index.js +11 -1
  59. package/lib/modules/Order/types.d.ts +2 -0
  60. package/lib/modules/ProductList/index.d.ts +3 -1
  61. package/lib/modules/ProductList/index.js +45 -39
  62. package/lib/modules/Schedule/getDateIsInSchedule.js +4 -0
  63. package/lib/solution/BookingByStep/index.d.ts +2 -2
  64. package/lib/solution/ScanOrder/index.js +1 -1
  65. package/lib/solution/ShopDiscount/index.js +3 -2
  66. package/lib/solution/VenueBooking/index.d.ts +40 -3
  67. package/lib/solution/VenueBooking/index.js +257 -111
  68. package/lib/solution/VenueBooking/types.d.ts +3 -1
  69. package/lib/solution/VenueBooking/types.js +1 -0
  70. package/lib/solution/VenueBooking/utils/dateSummary.d.ts +2 -2
  71. package/lib/solution/VenueBooking/utils/dateSummary.js +5 -10
  72. package/lib/solution/VenueBooking/utils/slotMerge.d.ts +0 -6
  73. package/lib/solution/VenueBooking/utils/slotMerge.js +3 -4
  74. package/lib/solution/VenueBooking/utils/smartPricing.d.ts +70 -0
  75. package/lib/solution/VenueBooking/utils/smartPricing.js +180 -0
  76. package/lib/solution/VenueBooking/utils/timeSlot.d.ts +2 -0
  77. package/lib/solution/VenueBooking/utils/timeSlot.js +7 -3
  78. package/package.json +1 -1
@@ -6,7 +6,9 @@ export declare enum VenueBookingHooks {
6
6
  onDestroy = "venueBooking:onDestroy",
7
7
  onRetryInit = "venueBooking:onRetryInit",
8
8
  onRefresh = "venueBooking:onRefresh",
9
- onCartValidationChanged = "venueBooking:onCartValidationChanged"
9
+ onCartValidationChanged = "venueBooking:onCartValidationChanged",
10
+ /** 登录客户的智能定价、营销配置与订单汇总已完成刷新 */
11
+ onSmartPricingUpdated = "venueBooking:onSmartPricingUpdated"
10
12
  }
11
13
  export type VenueBookingStatus = 'idle' | 'initializing' | 'ready' | 'error';
12
14
  export interface VenueBookingEntryContext {
@@ -4,6 +4,7 @@ export var VenueBookingHooks = /*#__PURE__*/function (VenueBookingHooks) {
4
4
  VenueBookingHooks["onRetryInit"] = "venueBooking:onRetryInit";
5
5
  VenueBookingHooks["onRefresh"] = "venueBooking:onRefresh";
6
6
  VenueBookingHooks["onCartValidationChanged"] = "venueBooking:onCartValidationChanged";
7
+ VenueBookingHooks["onSmartPricingUpdated"] = "venueBooking:onSmartPricingUpdated";
7
8
  return VenueBookingHooks;
8
9
  }({});
9
10
 
@@ -1,11 +1,11 @@
1
1
  import type { VenueBookingSlotConfig, VenueResourceRawData, VenueDateSummaryItem, ResourceProductMapping } from '../types';
2
- import type { QuotationModule } from '../../../modules/Quotation';
3
2
  export declare function buildDateRangeSummary(params: {
4
3
  startDate: string;
5
4
  endDate: string;
6
5
  config: VenueBookingSlotConfig;
7
6
  rawResources: VenueResourceRawData[];
8
7
  resourceProductMap: Map<number | string, ResourceProductMapping[]>;
9
- quotationModule?: QuotationModule;
10
8
  resolveConfig?: (date: string) => VenueBookingSlotConfig;
9
+ /** 按日期构建 slot 智能定价 Map */
10
+ buildSlotPriceMap?: (date: string, config: VenueBookingSlotConfig, productIds: number[]) => Map<string, string | null>;
11
11
  }): VenueDateSummaryItem[];
@@ -6,41 +6,31 @@ function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symb
6
6
  function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
7
7
  function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
8
8
  import dayjs from 'dayjs';
9
- import { buildTimeSlotGrid, generateTimeLabels } from "./timeSlot";
9
+ import { buildTimeSlotGrid } from "./timeSlot";
10
10
  export function buildDateRangeSummary(params) {
11
11
  var startDate = params.startDate,
12
12
  endDate = params.endDate,
13
13
  config = params.config,
14
14
  rawResources = params.rawResources,
15
15
  resourceProductMap = params.resourceProductMap,
16
- quotationModule = params.quotationModule,
17
- resolveConfig = params.resolveConfig;
16
+ resolveConfig = params.resolveConfig,
17
+ buildSlotPriceMap = params.buildSlotPriceMap;
18
18
  var result = [];
19
- var productIds = quotationModule ? _toConsumableArray(new Set(_toConsumableArray(resourceProductMap.values()).flat().map(function (m) {
19
+ var productIds = buildSlotPriceMap ? _toConsumableArray(new Set(_toConsumableArray(resourceProductMap.values()).flat().map(function (m) {
20
20
  return m.productId;
21
21
  }))) : [];
22
22
  var cursor = dayjs(startDate);
23
23
  var end = dayjs(endDate);
24
- var _loop = function _loop() {
25
- var date = cursor.format('YYYY-MM-DD');
26
- var effectiveConfig = (resolveConfig === null || resolveConfig === void 0 ? void 0 : resolveConfig(date)) || config;
27
- var quotationPriceMap;
28
- if (quotationModule && productIds.length) {
29
- var timeLabels = generateTimeLabels(effectiveConfig);
30
- var timePoints = timeLabels.map(function (label) {
31
- return "".concat(date, " ").concat(label);
32
- });
33
- quotationPriceMap = quotationModule.buildProductPriceMap({
34
- productIds: productIds,
35
- timePoints: timePoints
36
- });
37
- }
24
+ while (!cursor.isAfter(end)) {
25
+ var _date = cursor.format('YYYY-MM-DD');
26
+ var effectiveConfig = (resolveConfig === null || resolveConfig === void 0 ? void 0 : resolveConfig(_date)) || config;
27
+ var slotPriceMap = buildSlotPriceMap && productIds.length ? buildSlotPriceMap(_date, effectiveConfig, productIds) : undefined;
38
28
  var grid = buildTimeSlotGrid({
39
- date: date,
29
+ date: _date,
40
30
  config: effectiveConfig,
41
31
  rawResources: rawResources,
42
32
  resourceProductMap: resourceProductMap,
43
- quotationPriceMap: quotationPriceMap
33
+ slotPriceMap: slotPriceMap
44
34
  });
45
35
  var totalSlots = 0;
46
36
  var availableSlots = 0;
@@ -78,7 +68,7 @@ export function buildDateRangeSummary(params) {
78
68
  } finally {
79
69
  _iterator.f();
80
70
  }
81
- var status;
71
+ var status = void 0;
82
72
  if (totalSlots === 0) {
83
73
  status = 'unavailable';
84
74
  } else if (availableSlots === 0) {
@@ -89,16 +79,13 @@ export function buildDateRangeSummary(params) {
89
79
  status = 'available';
90
80
  }
91
81
  result.push({
92
- date: date,
82
+ date: _date,
93
83
  status: status,
94
84
  startingPrice: lowestPrice !== null ? lowestPrice.toFixed(2) : null,
95
85
  totalSlots: totalSlots,
96
86
  availableSlots: availableSlots
97
87
  });
98
88
  cursor = cursor.add(1, 'day');
99
- };
100
- while (!cursor.isAfter(end)) {
101
- _loop();
102
89
  }
103
90
  return result;
104
91
  }
@@ -5,12 +5,6 @@ export declare function mergeConsecutiveSlots(slots: VenueSlotSelection[]): Merg
5
5
  export declare function buildPriceBreakdown(params: {
6
6
  group: MergedSlotGroup;
7
7
  productId: number;
8
- quotation?: {
9
- getQuotationShelfId: (p: {
10
- productId: number;
11
- datetime: string;
12
- }) => number;
13
- };
14
8
  }): PriceBreakdownEntry[];
15
9
  export interface BuildVenueBookingParams {
16
10
  group: MergedSlotGroup;
@@ -51,9 +51,7 @@ export function mergeConsecutiveSlots(slots) {
51
51
  return groups;
52
52
  }
53
53
  export function buildPriceBreakdown(params) {
54
- var group = params.group,
55
- productId = params.productId,
56
- quotation = params.quotation;
54
+ var group = params.group;
57
55
  if (!group.slots.length) return [];
58
56
  var entries = [];
59
57
  var _iterator = _createForOfIteratorHelper(group.slots),
@@ -62,14 +60,10 @@ export function buildPriceBreakdown(params) {
62
60
  for (_iterator.s(); !(_step = _iterator.n()).done;) {
63
61
  var slot = _step.value;
64
62
  var unitPrice = Number(slot.price || '0');
65
- var shelfId = quotation ? quotation.getQuotationShelfId({
66
- productId: productId,
67
- datetime: slot.startTime
68
- }) : 0;
69
63
  var startHm = dayjs(slot.startTime, 'YYYY-MM-DD HH:mm').format('YYYY-MM-DD HH:mm');
70
64
  var endHm = dayjs(slot.endTime, 'YYYY-MM-DD HH:mm').format('YYYY-MM-DD HH:mm');
71
65
  var last = entries[entries.length - 1];
72
- if (last && last.unit_price === unitPrice && last.quotation_shelf_id === shelfId && last.end_time === startHm) {
66
+ if (last && last.unit_price === unitPrice && last.quotation_shelf_id === 0 && last.end_time === startHm) {
73
67
  last.end_time = endHm;
74
68
  last.quantity += 1;
75
69
  } else {
@@ -77,7 +71,7 @@ export function buildPriceBreakdown(params) {
77
71
  start_time: startHm,
78
72
  end_time: endHm,
79
73
  unit_price: unitPrice,
80
- quotation_shelf_id: shelfId,
74
+ quotation_shelf_id: 0,
81
75
  quantity: 1
82
76
  });
83
77
  }
@@ -0,0 +1,70 @@
1
+ import type { ProductData } from '../../../modules/Product/types';
2
+ import type { ScheduleItem } from '../../../modules/Schedule/types';
3
+ import type { DataVariantBusinessData, DataVariantMenu, DataVariantProduct } from '../../../model/strategy/adapter/dataVariant/type';
4
+ import type { DataVariantEvaluator } from '../../../model/strategy/adapter/dataVariant/evaluator';
5
+ import type { StrategyConfig } from '../../../model/strategy/type';
6
+ import type { VenueBookingSlotConfig } from '../types';
7
+ /** VenueBooking 智能定价运行时上下文 */
8
+ export interface VenueBookingSmartPricingContext {
9
+ evaluator?: DataVariantEvaluator | null;
10
+ scheduleList?: ScheduleItem[];
11
+ menuList?: DataVariantMenu[];
12
+ customerId?: number | string;
13
+ availableWalletIds?: Array<number | string>;
14
+ channel?: string;
15
+ businessCode?: string;
16
+ orderType?: string;
17
+ strategy_context?: Record<string, any>;
18
+ strategyConfigs?: StrategyConfig[];
19
+ }
20
+ /** 从商品上读取展示/算价用的 price 字段 */
21
+ export declare function extractProductPrice(product: DataVariantProduct): string | null;
22
+ /** 组装 DataVariant 评估入参,对齐 OS Server buildProductFormatterContext 语义 */
23
+ export declare function buildDataVariantBusinessData(products: ProductData[], context: VenueBookingSmartPricingContext, scheduleDateTime: string): DataVariantBusinessData;
24
+ /**
25
+ * 对商品列表执行一次智能定价格式化(loadProducts 后使用)。
26
+ * evaluator 未注入时原样返回并打 warning。
27
+ */
28
+ export declare function formatProductsWithDataVariant(products: ProductData[], context: VenueBookingSmartPricingContext, scheduleDateTime?: string): ProductData[];
29
+ /**
30
+ * 根据 schedule_time_points 与营业开始时间,构造当天需要评估的 datetime 列表(升序)。
31
+ * 跨日营业时,早于营业开始小时的时间点归到次日。
32
+ */
33
+ export declare function buildEvaluationDatetimes(params: {
34
+ date: string;
35
+ config: VenueBookingSlotConfig;
36
+ scheduleTimePoints: string[];
37
+ }): string[];
38
+ /** 找到 slot 时间所属分段的评估 datetime(max(evalDt <= slotDt)) */
39
+ export declare function findSegmentEvalDatetime(slotDatetime: string, evalDatetimes: string[]): string;
40
+ /** 将 slot 标签转为与 timeSlot 网格一致的 slotStart 字符串 */
41
+ export declare function buildSlotStartDatetime(date: string, label: string, config: VenueBookingSlotConfig): string;
42
+ /**
43
+ * 按 schedule 分段批量 resolveProducts,输出与 quotation 相同 key 格式的 slot 价格 Map。
44
+ * key: `${productId}:${slotStart}`,value: price | null
45
+ */
46
+ export declare function buildSlotPriceMapByScheduleSegments(params: {
47
+ products: ProductData[];
48
+ productIds: number[];
49
+ date: string;
50
+ timeLabels: string[];
51
+ config: VenueBookingSlotConfig;
52
+ context: VenueBookingSmartPricingContext;
53
+ }): Map<string, string | null>;
54
+ /**
55
+ * 指定商品在某一 datetime 的智能定价(addon / 单点重算用)。
56
+ */
57
+ export declare function getPriceForProductAtDatetime(params: {
58
+ products: ProductData[];
59
+ context: VenueBookingSmartPricingContext;
60
+ productId: number;
61
+ datetime: string;
62
+ fallbackPrice?: string;
63
+ }): string;
64
+ /** 从 slot 价格 Map 读取单价,找不到则回退 fallback */
65
+ export declare function getSlotPriceFromMap(params: {
66
+ slotPriceMap: Map<string, string | null>;
67
+ productId: number;
68
+ slotStartTime: string;
69
+ fallbackPrice?: string;
70
+ }): string;
@@ -0,0 +1,233 @@
1
+ function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
2
+ function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
3
+ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
4
+ import dayjs from 'dayjs';
5
+ import { isBusinessHoursCrossDay } from "./timeSlot";
6
+
7
+ /** VenueBooking 智能定价运行时上下文 */
8
+
9
+ /** 从商品上读取展示/算价用的 price 字段 */
10
+ export function extractProductPrice(product) {
11
+ var _product$price;
12
+ var price = (_product$price = product === null || product === void 0 ? void 0 : product.price) !== null && _product$price !== void 0 ? _product$price : product === null || product === void 0 ? void 0 : product.selling_price;
13
+ if (price == null || price === '') return null;
14
+ return String(price);
15
+ }
16
+
17
+ /** 组装 DataVariant 评估入参,对齐 OS Server buildProductFormatterContext 语义 */
18
+ export function buildDataVariantBusinessData(products, context, scheduleDateTime) {
19
+ var _ref, _context$availableWal, _context$channel, _ref2, _context$orderType, _ref3, _context$businessCode;
20
+ var strategyContext = context.strategy_context || {};
21
+ return {
22
+ products: products,
23
+ strategyConfigs: context.strategyConfigs,
24
+ scheduleDateTime: scheduleDateTime,
25
+ scheduleList: context.scheduleList || [],
26
+ menuList: context.menuList || [],
27
+ customerId: context.customerId,
28
+ availableWalletIds: (_ref = (_context$availableWal = context.availableWalletIds) !== null && _context$availableWal !== void 0 ? _context$availableWal : strategyContext.available_wallet_ids) !== null && _ref !== void 0 ? _ref : strategyContext.availableWalletIds,
29
+ channel: (_context$channel = context.channel) !== null && _context$channel !== void 0 ? _context$channel : strategyContext.channel,
30
+ orderType: (_ref2 = (_context$orderType = context.orderType) !== null && _context$orderType !== void 0 ? _context$orderType : strategyContext.orderType) !== null && _ref2 !== void 0 ? _ref2 : strategyContext.order_type,
31
+ businessCode: (_ref3 = (_context$businessCode = context.businessCode) !== null && _context$businessCode !== void 0 ? _context$businessCode : strategyContext.business_code) !== null && _ref3 !== void 0 ? _ref3 : strategyContext.businessCode,
32
+ custom: strategyContext
33
+ };
34
+ }
35
+
36
+ /**
37
+ * 对商品列表执行一次智能定价格式化(loadProducts 后使用)。
38
+ * evaluator 未注入时原样返回并打 warning。
39
+ */
40
+ export function formatProductsWithDataVariant(products, context, scheduleDateTime) {
41
+ var evaluator = context.evaluator;
42
+ if (!evaluator || typeof evaluator.resolveProducts !== 'function') {
43
+ console.warn('[VenueBooking][smartPricing] dataVariantEvaluator 未注入,跳过智能定价格式化');
44
+ return products;
45
+ }
46
+ var resolvedAt = scheduleDateTime || dayjs().format('YYYY-MM-DD HH:mm:ss');
47
+ var businessData = buildDataVariantBusinessData(products, context, resolvedAt);
48
+ var result = evaluator.resolveProducts(businessData);
49
+ return Array.isArray(result === null || result === void 0 ? void 0 : result.products) ? result.products : products;
50
+ }
51
+
52
+ /**
53
+ * 根据 schedule_time_points 与营业开始时间,构造当天需要评估的 datetime 列表(升序)。
54
+ * 跨日营业时,早于营业开始小时的时间点归到次日。
55
+ */
56
+ export function buildEvaluationDatetimes(params) {
57
+ var date = params.date,
58
+ config = params.config,
59
+ scheduleTimePoints = params.scheduleTimePoints;
60
+ var crossDay = isBusinessHoursCrossDay(config);
61
+ var businessStartHour = Number(config.businessStartTime.split(':')[0]);
62
+ var evalSet = new Set();
63
+ evalSet.add("".concat(date, " ").concat(config.businessStartTime));
64
+ var _iterator = _createForOfIteratorHelper(scheduleTimePoints),
65
+ _step;
66
+ try {
67
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
68
+ var timePoint = _step.value;
69
+ var hour = Number(timePoint.split(':')[0]);
70
+ var pointDate = crossDay && hour < businessStartHour ? dayjs(date).add(1, 'day').format('YYYY-MM-DD') : date;
71
+ evalSet.add("".concat(pointDate, " ").concat(timePoint));
72
+ }
73
+ } catch (err) {
74
+ _iterator.e(err);
75
+ } finally {
76
+ _iterator.f();
77
+ }
78
+ return Array.from(evalSet).sort(function (a, b) {
79
+ var aMs = dayjs(a, ['YYYY-MM-DD HH:mm:ss', 'YYYY-MM-DD HH:mm']).valueOf();
80
+ var bMs = dayjs(b, ['YYYY-MM-DD HH:mm:ss', 'YYYY-MM-DD HH:mm']).valueOf();
81
+ return aMs - bMs;
82
+ });
83
+ }
84
+
85
+ /** 找到 slot 时间所属分段的评估 datetime(max(evalDt <= slotDt)) */
86
+ export function findSegmentEvalDatetime(slotDatetime, evalDatetimes) {
87
+ if (!evalDatetimes.length) return slotDatetime;
88
+ var slotMs = dayjs(slotDatetime, ['YYYY-MM-DD HH:mm:ss', 'YYYY-MM-DD HH:mm']).valueOf();
89
+ var chosen = evalDatetimes[0];
90
+ var _iterator2 = _createForOfIteratorHelper(evalDatetimes),
91
+ _step2;
92
+ try {
93
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
94
+ var evalDt = _step2.value;
95
+ var evalMs = dayjs(evalDt, ['YYYY-MM-DD HH:mm:ss', 'YYYY-MM-DD HH:mm']).valueOf();
96
+ if (evalMs <= slotMs) {
97
+ chosen = evalDt;
98
+ } else {
99
+ break;
100
+ }
101
+ }
102
+ } catch (err) {
103
+ _iterator2.e(err);
104
+ } finally {
105
+ _iterator2.f();
106
+ }
107
+ return chosen;
108
+ }
109
+
110
+ /** 将 slot 标签转为与 timeSlot 网格一致的 slotStart 字符串 */
111
+ export function buildSlotStartDatetime(date, label, config) {
112
+ var crossDay = isBusinessHoursCrossDay(config);
113
+ var businessStartHour = Number(config.businessStartTime.split(':')[0]);
114
+ var hour = Number(label.split(':')[0]);
115
+ var slotDate = crossDay && hour < businessStartHour ? dayjs(date).add(1, 'day').format('YYYY-MM-DD') : date;
116
+ return dayjs("".concat(slotDate, " ").concat(label)).format('YYYY-MM-DD HH:mm');
117
+ }
118
+
119
+ /**
120
+ * 按 schedule 分段批量 resolveProducts,输出与 quotation 相同 key 格式的 slot 价格 Map。
121
+ * key: `${productId}:${slotStart}`,value: price | null
122
+ */
123
+ export function buildSlotPriceMapByScheduleSegments(params) {
124
+ var products = params.products,
125
+ productIds = params.productIds,
126
+ date = params.date,
127
+ timeLabels = params.timeLabels,
128
+ config = params.config,
129
+ context = params.context;
130
+ var map = new Map();
131
+ if (!productIds.length || !timeLabels.length) return map;
132
+ var evaluator = context.evaluator;
133
+ if (!evaluator || typeof evaluator.resolveProducts !== 'function') {
134
+ return map;
135
+ }
136
+ var strategyConfigs = context.strategyConfigs || (typeof evaluator.getStrategyConfigs === 'function' ? evaluator.getStrategyConfigs() : []);
137
+ var scheduleTimePoints = typeof evaluator.buildScheduleTimePoints === 'function' ? evaluator.buildScheduleTimePoints(strategyConfigs, context.scheduleList || []) : [];
138
+ var evalDatetimes = buildEvaluationDatetimes({
139
+ date: date,
140
+ config: config,
141
+ scheduleTimePoints: scheduleTimePoints
142
+ });
143
+ var priceCache = new Map();
144
+ var _iterator3 = _createForOfIteratorHelper(evalDatetimes),
145
+ _step3;
146
+ try {
147
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
148
+ var evalDt = _step3.value;
149
+ var resolved = formatProductsWithDataVariant(products, context, evalDt);
150
+ var priceByProductId = new Map();
151
+ var _iterator5 = _createForOfIteratorHelper(resolved),
152
+ _step5;
153
+ try {
154
+ for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
155
+ var product = _step5.value;
156
+ var productId = Number(product.id);
157
+ if (!Number.isFinite(productId)) continue;
158
+ priceByProductId.set(productId, extractProductPrice(product));
159
+ }
160
+ } catch (err) {
161
+ _iterator5.e(err);
162
+ } finally {
163
+ _iterator5.f();
164
+ }
165
+ priceCache.set(evalDt, priceByProductId);
166
+ }
167
+ } catch (err) {
168
+ _iterator3.e(err);
169
+ } finally {
170
+ _iterator3.f();
171
+ }
172
+ var _iterator4 = _createForOfIteratorHelper(timeLabels),
173
+ _step4;
174
+ try {
175
+ for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
176
+ var label = _step4.value;
177
+ var slotStart = buildSlotStartDatetime(date, label, config);
178
+ var segmentEvalDt = findSegmentEvalDatetime(slotStart, evalDatetimes);
179
+ var _priceByProductId = priceCache.get(segmentEvalDt);
180
+ var _iterator6 = _createForOfIteratorHelper(productIds),
181
+ _step6;
182
+ try {
183
+ for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) {
184
+ var _priceByProductId$get;
185
+ var _productId = _step6.value;
186
+ var key = "".concat(_productId, ":").concat(slotStart);
187
+ var price = (_priceByProductId$get = _priceByProductId === null || _priceByProductId === void 0 ? void 0 : _priceByProductId.get(_productId)) !== null && _priceByProductId$get !== void 0 ? _priceByProductId$get : null;
188
+ if (price !== null) {
189
+ map.set(key, price);
190
+ }
191
+ }
192
+ } catch (err) {
193
+ _iterator6.e(err);
194
+ } finally {
195
+ _iterator6.f();
196
+ }
197
+ }
198
+ } catch (err) {
199
+ _iterator4.e(err);
200
+ } finally {
201
+ _iterator4.f();
202
+ }
203
+ return map;
204
+ }
205
+
206
+ /**
207
+ * 指定商品在某一 datetime 的智能定价(addon / 单点重算用)。
208
+ */
209
+ export function getPriceForProductAtDatetime(params) {
210
+ var _ref4, _extractProductPrice;
211
+ var products = params.products,
212
+ context = params.context,
213
+ productId = params.productId,
214
+ datetime = params.datetime,
215
+ fallbackPrice = params.fallbackPrice;
216
+ var resolved = formatProductsWithDataVariant(products, context, datetime);
217
+ var product = resolved.find(function (item) {
218
+ return Number(item.id) === Number(productId);
219
+ });
220
+ return (_ref4 = (_extractProductPrice = extractProductPrice(product || {})) !== null && _extractProductPrice !== void 0 ? _extractProductPrice : fallbackPrice) !== null && _ref4 !== void 0 ? _ref4 : '0.00';
221
+ }
222
+
223
+ /** 从 slot 价格 Map 读取单价,找不到则回退 fallback */
224
+ export function getSlotPriceFromMap(params) {
225
+ var slotPriceMap = params.slotPriceMap,
226
+ productId = params.productId,
227
+ slotStartTime = params.slotStartTime,
228
+ fallbackPrice = params.fallbackPrice;
229
+ var key = "".concat(productId, ":").concat(slotStartTime);
230
+ var mapped = slotPriceMap.get(key);
231
+ if (mapped != null) return mapped;
232
+ return fallbackPrice !== null && fallbackPrice !== void 0 ? fallbackPrice : '0.00';
233
+ }
@@ -27,6 +27,8 @@ export declare function buildTimeSlotGrid(params: {
27
27
  config: VenueBookingSlotConfig;
28
28
  rawResources: VenueResourceRawData[];
29
29
  resourceProductMap: Map<number | string, ResourceProductMapping[]>;
30
+ /** @deprecated 使用 slotPriceMap */
30
31
  quotationPriceMap?: Map<string, string | null>;
32
+ slotPriceMap?: Map<string, string | null>;
31
33
  }): VenueTimeSlotGrid;
32
34
  export {};
@@ -230,6 +230,7 @@ function buildProductSlots(params) {
230
230
  now = params.now,
231
231
  crossDay = params.crossDay,
232
232
  quotationPriceMap = params.quotationPriceMap,
233
+ slotPriceMap = params.slotPriceMap,
233
234
  childRawResources = params.childRawResources,
234
235
  childTimesCache = params.childTimesCache,
235
236
  childEventsCache = params.childEventsCache,
@@ -239,7 +240,7 @@ function buildProductSlots(params) {
239
240
  _step7;
240
241
  try {
241
242
  for (_iterator7.s(); !(_step7 = _iterator7.n()).done;) {
242
- var _quotationPriceMap$ge;
243
+ var _resolvedPriceMap$get;
243
244
  var label = _step7.value;
244
245
  var _label$split$map = label.split(':').map(Number),
245
246
  _label$split$map2 = _slicedToArray(_label$split$map, 1),
@@ -298,11 +299,12 @@ function buildProductSlots(params) {
298
299
  }
299
300
  var isBookable = status === 'available' || status === 'partially_occupied';
300
301
  var slotStartStr = slotStart.format('YYYY-MM-DD HH:mm');
302
+ var resolvedPriceMap = slotPriceMap !== null && slotPriceMap !== void 0 ? slotPriceMap : quotationPriceMap;
301
303
  slots.push({
302
304
  startTime: slotStartStr,
303
305
  endTime: slotEnd.format('YYYY-MM-DD HH:mm'),
304
306
  status: status,
305
- price: isBookable ? (_quotationPriceMap$ge = quotationPriceMap === null || quotationPriceMap === void 0 ? void 0 : quotationPriceMap.get("".concat(mapping.productId, ":").concat(slotStartStr))) !== null && _quotationPriceMap$ge !== void 0 ? _quotationPriceMap$ge : mapping.price : null,
307
+ price: isBookable ? (_resolvedPriceMap$get = resolvedPriceMap === null || resolvedPriceMap === void 0 ? void 0 : resolvedPriceMap.get("".concat(mapping.productId, ":").concat(slotStartStr))) !== null && _resolvedPriceMap$get !== void 0 ? _resolvedPriceMap$get : mapping.price : null,
306
308
  resourceId: resource.resourceId,
307
309
  resourceFormId: resource.formId,
308
310
  capacity: status === 'past' || status === 'unavailable' ? null : resCapacity,
@@ -322,7 +324,9 @@ export function buildTimeSlotGrid(params) {
322
324
  config = params.config,
323
325
  rawResources = params.rawResources,
324
326
  resourceProductMap = params.resourceProductMap,
325
- quotationPriceMap = params.quotationPriceMap;
327
+ quotationPriceMap = params.quotationPriceMap,
328
+ slotPriceMap = params.slotPriceMap;
329
+ var resolvedPriceMap = slotPriceMap !== null && slotPriceMap !== void 0 ? slotPriceMap : quotationPriceMap;
326
330
  var timeLabels = generateTimeLabels(config);
327
331
  var now = dayjs();
328
332
  var crossDay = isBusinessHoursCrossDay(config);
@@ -377,7 +381,8 @@ export function buildTimeSlotGrid(params) {
377
381
  resCapacity: resCapacity,
378
382
  now: now,
379
383
  crossDay: crossDay,
380
- quotationPriceMap: quotationPriceMap,
384
+ quotationPriceMap: resolvedPriceMap,
385
+ slotPriceMap: resolvedPriceMap,
381
386
  childRawResources: childRawResources,
382
387
  childTimesCache: childTimesCache,
383
388
  childEventsCache: childEventsCache,
package/lib/index.d.ts CHANGED
@@ -7,5 +7,6 @@ export * from './modules';
7
7
  export * from './plugins';
8
8
  export * from './solution';
9
9
  export { PisellOSCore } from './core';
10
+ export { DataVariantEvaluator } from './model/strategy/adapter/dataVariant/evaluator';
10
11
  import PisellOSCore from './core';
11
12
  export default PisellOSCore;
package/lib/index.js CHANGED
@@ -30,6 +30,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var src_exports = {};
32
32
  __export(src_exports, {
33
+ DataVariantEvaluator: () => import_evaluator.DataVariantEvaluator,
33
34
  PisellOSCore: () => import_core.PisellOSCore,
34
35
  default: () => src_default
35
36
  });
@@ -39,10 +40,12 @@ __reExport(src_exports, require("./modules"), module.exports);
39
40
  __reExport(src_exports, require("./plugins"), module.exports);
40
41
  __reExport(src_exports, require("./solution"), module.exports);
41
42
  var import_core = require("./core");
43
+ var import_evaluator = require("./model/strategy/adapter/dataVariant/evaluator");
42
44
  var import_core2 = __toESM(require("./core"));
43
45
  var src_default = import_core2.default;
44
46
  // Annotate the CommonJS export names for ESM import in node:
45
47
  0 && (module.exports = {
48
+ DataVariantEvaluator,
46
49
  PisellOSCore,
47
50
  ...require("./types"),
48
51
  ...require("./modules"),
@@ -0,0 +1,50 @@
1
+ import type { EvaluationResult, RuntimeContext } from '../../type';
2
+ import type { BusinessAdapter } from '../type';
3
+ import type { DataVariantBusinessData, DataVariantProduct } from './type';
4
+ /**
5
+ * Data Variant 适配器。
6
+ *
7
+ * 负责把智能定价运行态数据转换为 StrategyEngine 能识别的 attributes。
8
+ */
9
+ export declare class DataVariantAdapter implements BusinessAdapter {
10
+ name: string;
11
+ version: string;
12
+ /**
13
+ * 准备批量全局上下文。
14
+ *
15
+ * 这里只放不依赖单个商品的字段,用于预计算 schedule/channel/orderType 等条件。
16
+ */
17
+ prepareContext(businessData: DataVariantBusinessData): RuntimeContext;
18
+ /**
19
+ * 准备全局预计算上下文。
20
+ */
21
+ prepareGlobalContext(businessData: DataVariantBusinessData): RuntimeContext;
22
+ /**
23
+ * 准备单商品上下文。
24
+ *
25
+ * 商品级条件只在当前商品 data_variants 引用到对应规则后才评估。
26
+ */
27
+ prepareProductContext(businessData: DataVariantBusinessData, product: DataVariantProduct): RuntimeContext;
28
+ transformResult(result: EvaluationResult): EvaluationResult;
29
+ formatConfig(result: EvaluationResult, businessData?: DataVariantBusinessData): {
30
+ result: EvaluationResult;
31
+ businessData?: DataVariantBusinessData;
32
+ };
33
+ /**
34
+ * 构建 menu_match 使用的轻量商品对象。
35
+ *
36
+ * collection_ids 来自商品 collection 字段,避免 operator 读取复杂商品对象。
37
+ */
38
+ buildProductValue(product: DataVariantProduct): {
39
+ id: number;
40
+ product_variant_id: number;
41
+ collection_ids: number[];
42
+ };
43
+ private toNumber;
44
+ /**
45
+ * 归一化客户 id:无效值返回 undefined,有效值统一为 number。
46
+ */
47
+ private normalizeCustomerId;
48
+ private normalizeNumberArray;
49
+ }
50
+ export default DataVariantAdapter;