@pisell/pisellos 2.2.301 → 2.2.303
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.
- package/dist/model/strategy/adapter/dataVariant/evaluator.d.ts +18 -2
- package/dist/model/strategy/adapter/dataVariant/evaluator.js +287 -101
- package/dist/model/strategy/adapter/dataVariant/type.d.ts +25 -0
- package/dist/model/strategy/adapter/itemRule/adapter.d.ts +4 -1
- package/dist/model/strategy/adapter/itemRule/adapter.js +135 -35
- package/dist/model/strategy/adapter/itemRule/evaluator.d.ts +8 -1
- package/dist/model/strategy/adapter/itemRule/evaluator.js +25 -1
- package/dist/model/strategy/adapter/itemRule/type.d.ts +44 -0
- package/dist/model/strategy/adapter/itemRule/type.js +19 -1
- package/dist/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +10 -7
- package/dist/modules/Discount/index.d.ts +2 -0
- package/dist/modules/Discount/index.js +41 -5
- package/dist/modules/Discount/types.d.ts +21 -0
- package/dist/modules/OpenData/index.d.ts +15 -2
- package/dist/modules/OpenData/index.js +307 -19
- package/dist/modules/OpenData/types.d.ts +55 -0
- package/dist/modules/OpenData/types.js +7 -1
- package/dist/modules/OpenData/utils.d.ts +21 -1
- package/dist/modules/OpenData/utils.js +138 -0
- package/dist/modules/Order/index.d.ts +50 -7
- package/dist/modules/Order/index.js +1690 -815
- package/dist/modules/Order/salesNotes.d.ts +31 -0
- package/dist/modules/Order/salesNotes.js +492 -0
- package/dist/modules/Order/types.d.ts +117 -10
- package/dist/modules/Order/types.js +6 -1
- package/dist/modules/Order/utils/virtualSettlement.d.ts +63 -0
- package/dist/modules/Order/utils/virtualSettlement.js +284 -0
- package/dist/modules/Order/utils.d.ts +5 -1
- package/dist/modules/Order/utils.js +138 -33
- package/dist/modules/Payment/walletpass.js +20 -21
- package/dist/modules/Product/types.d.ts +23 -0
- package/dist/modules/ProductList/index.d.ts +2 -1
- package/dist/modules/ProductList/index.js +100 -20
- package/dist/modules/ProductList/types.d.ts +10 -0
- package/dist/modules/Rules/index.d.ts +5 -0
- package/dist/modules/Rules/index.js +30 -14
- package/dist/modules/SalesSummary/index.d.ts +1 -0
- package/dist/modules/SalesSummary/index.js +4 -3
- package/dist/modules/SalesSummary/types.d.ts +1 -0
- package/dist/modules/SalesSummary/utils.d.ts +1 -0
- package/dist/modules/SalesSummary/utils.js +21 -1
- package/dist/plugins/request.d.ts +2 -1
- package/dist/plugins/request.js +4 -2
- package/dist/server/index.d.ts +21 -0
- package/dist/server/index.js +1774 -1285
- package/dist/server/modules/order/types.d.ts +50 -0
- package/dist/server/modules/order/types.js +2 -0
- package/dist/server/utils/small-ticket.d.ts +4 -1
- package/dist/server/utils/small-ticket.js +461 -96
- package/dist/solution/BaseSales/index.d.ts +26 -3
- package/dist/solution/BaseSales/index.js +1492 -873
- package/dist/solution/BaseSales/utils/parseSalesResponse.d.ts +2 -0
- package/dist/solution/BaseSales/utils/parseSalesResponse.js +1 -1
- package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.d.ts +1 -0
- package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +72 -9
- package/dist/solution/BookingByStep/index.d.ts +1 -1
- package/dist/solution/BookingTicket/index.d.ts +8 -3
- package/dist/solution/BookingTicket/index.js +558 -443
- package/dist/solution/BookingTicket/utils/addProductDecision.js +2 -1
- package/dist/solution/BookingTicket/utils/orderCustomer.js +3 -2
- package/dist/solution/RegisterAndLogin/config.d.ts +9 -3
- package/dist/solution/RegisterAndLogin/config.js +95 -40
- package/dist/solution/RegisterAndLogin/index.js +4 -1
- package/dist/solution/Sales/index.d.ts +16 -1
- package/dist/solution/Sales/index.js +338 -63
- package/dist/solution/Sales/types.d.ts +10 -0
- package/dist/solution/ScanOrder/index.d.ts +1 -0
- package/dist/solution/ScanOrder/index.js +31 -27
- package/dist/solution/ScanOrder/utils.d.ts +1 -0
- package/dist/solution/ScanOrder/utils.js +2 -0
- package/dist/solution/UnifiedBookingSales/index.d.ts +20 -6
- package/dist/solution/UnifiedBookingSales/index.js +874 -527
- package/dist/solution/UnifiedBookingSales/types.d.ts +21 -2
- package/dist/solution/VenueBooking/index.d.ts +1 -0
- package/dist/solution/VenueBooking/index.js +8 -4
- package/lib/model/strategy/adapter/dataVariant/evaluator.d.ts +18 -2
- package/lib/model/strategy/adapter/dataVariant/evaluator.js +137 -6
- package/lib/model/strategy/adapter/dataVariant/type.d.ts +25 -0
- package/lib/model/strategy/adapter/itemRule/adapter.d.ts +4 -1
- package/lib/model/strategy/adapter/itemRule/adapter.js +73 -4
- package/lib/model/strategy/adapter/itemRule/evaluator.d.ts +8 -1
- package/lib/model/strategy/adapter/itemRule/evaluator.js +23 -1
- package/lib/model/strategy/adapter/itemRule/type.d.ts +44 -0
- package/lib/model/strategy/adapter/itemRule/type.js +19 -1
- package/lib/model/strategy/adapter/promotion/index.js +1 -46
- package/lib/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +4 -0
- package/lib/modules/Discount/index.d.ts +2 -0
- package/lib/modules/Discount/index.js +34 -2
- package/lib/modules/Discount/types.d.ts +21 -0
- package/lib/modules/OpenData/index.d.ts +15 -2
- package/lib/modules/OpenData/index.js +230 -3
- package/lib/modules/OpenData/types.d.ts +55 -0
- package/lib/modules/OpenData/types.js +9 -1
- package/lib/modules/OpenData/utils.d.ts +21 -1
- package/lib/modules/OpenData/utils.js +126 -0
- package/lib/modules/Order/index.d.ts +50 -7
- package/lib/modules/Order/index.js +704 -64
- package/lib/modules/Order/salesNotes.d.ts +31 -0
- package/lib/modules/Order/salesNotes.js +382 -0
- package/lib/modules/Order/types.d.ts +117 -10
- package/lib/modules/Order/types.js +5 -1
- package/lib/modules/Order/utils/virtualSettlement.d.ts +63 -0
- package/lib/modules/Order/utils/virtualSettlement.js +262 -0
- package/lib/modules/Order/utils.d.ts +5 -1
- package/lib/modules/Order/utils.js +127 -21
- package/lib/modules/Payment/walletpass.js +14 -11
- package/lib/modules/Product/types.d.ts +23 -0
- package/lib/modules/ProductList/index.d.ts +2 -1
- package/lib/modules/ProductList/index.js +47 -2
- package/lib/modules/ProductList/types.d.ts +10 -0
- package/lib/modules/Rules/index.d.ts +5 -0
- package/lib/modules/Rules/index.js +22 -12
- package/lib/modules/SalesSummary/index.d.ts +1 -0
- package/lib/modules/SalesSummary/index.js +4 -2
- package/lib/modules/SalesSummary/types.d.ts +1 -0
- package/lib/modules/SalesSummary/utils.d.ts +1 -0
- package/lib/modules/SalesSummary/utils.js +17 -1
- package/lib/plugins/request.d.ts +2 -1
- package/lib/plugins/request.js +3 -2
- package/lib/server/index.d.ts +21 -0
- package/lib/server/index.js +492 -108
- package/lib/server/modules/order/types.d.ts +50 -0
- package/lib/server/modules/order/types.js +1 -0
- package/lib/server/utils/small-ticket.d.ts +4 -1
- package/lib/server/utils/small-ticket.js +427 -72
- package/lib/solution/BaseSales/index.d.ts +26 -3
- package/lib/solution/BaseSales/index.js +439 -30
- package/lib/solution/BaseSales/utils/parseSalesResponse.d.ts +2 -0
- package/lib/solution/BaseSales/utils/parseSalesResponse.js +1 -1
- package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.d.ts +1 -0
- package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +38 -0
- package/lib/solution/BookingByStep/index.d.ts +1 -1
- package/lib/solution/BookingTicket/index.d.ts +8 -3
- package/lib/solution/BookingTicket/index.js +60 -15
- package/lib/solution/BookingTicket/utils/addProductDecision.js +1 -0
- package/lib/solution/BookingTicket/utils/orderCustomer.js +2 -1
- package/lib/solution/RegisterAndLogin/config.d.ts +9 -3
- package/lib/solution/RegisterAndLogin/config.js +49 -8
- package/lib/solution/RegisterAndLogin/index.js +4 -1
- package/lib/solution/Sales/index.d.ts +16 -1
- package/lib/solution/Sales/index.js +168 -1
- package/lib/solution/Sales/types.d.ts +10 -0
- package/lib/solution/ScanOrder/index.d.ts +1 -0
- package/lib/solution/ScanOrder/index.js +5 -1
- package/lib/solution/ScanOrder/utils.d.ts +1 -0
- package/lib/solution/ScanOrder/utils.js +1 -0
- package/lib/solution/UnifiedBookingSales/index.d.ts +20 -6
- package/lib/solution/UnifiedBookingSales/index.js +169 -16
- package/lib/solution/UnifiedBookingSales/types.d.ts +21 -2
- package/lib/solution/VenueBooking/index.d.ts +1 -0
- package/lib/solution/VenueBooking/index.js +5 -1
- package/package.json +1 -1
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
import type { OpenDataConfig } from '../../modules/OpenData';
|
|
2
|
-
import type { ProductListLoadProductsParams } from '../../modules/ProductList';
|
|
1
|
+
import type { InvoiceLayoutConfig, InvoiceTemplateSettings, OpenDataConfig } from '../../modules/OpenData';
|
|
2
|
+
import type { ProductListLoadProductsParams, ProductListStoreQueryParams } from '../../modules/ProductList';
|
|
3
|
+
import type { ProductData } from '../../modules/Product/types';
|
|
3
4
|
import type { LoadScheduleAvailableDateParams } from '../../modules/Schedule/types';
|
|
4
5
|
import type { AvailabilityDateRange, AvailabilityErrorCode, AvailabilityId, AvailabilityOccupancy, AvailabilityProduct, AvailabilityProjection, AvailabilityQuery, AvailabilityRequirementSelection, AvailabilityResource, AvailabilitySelectionValidationResult, DateAvailabilityQueryResult, GetProductTimeRangesRequest, ProductAvailabilityQueryResult, ProductTimeRange, ProductTimeRangeGroup, ResourceAvailabilityGroup, ResourceAvailabilityRangeGroup, TimeAvailabilityGroup, TimeAvailabilityItem } from '../../modules/ResourcePlanner';
|
|
5
6
|
import type { BookingTicketState } from '../BookingTicket/types';
|
|
7
|
+
import type { AllergyRequirement } from '../../model/strategy/adapter/itemRule';
|
|
8
|
+
import type { SalesNoteDraftInput } from '../../modules/Order/types';
|
|
9
|
+
export type { ProductListStoreQueryParams } from '../../modules/ProductList';
|
|
6
10
|
export type { AvailabilityAbsoluteRange, AvailabilityAssignment, AvailabilityCell, AvailabilityDateRange, AvailabilityDisplayRange, AvailabilityErrorCode, AvailabilityId, AvailabilityMode, AvailabilityOccupancy, AvailabilityProduct, AvailabilityProductKind, AvailabilityProjection, AvailabilityProjectionInput, AvailabilityProjectionMeta, AvailabilityQuery, AvailabilityQueryResult, AvailabilityRequirementGroup, AvailabilityRequirementGroupResult, AvailabilityRequirementSelection, AvailabilityResource, AvailabilityResourceOption, AvailabilityResourceType, AvailabilityResourceWindow, AvailabilityScheduleMode, AvailabilityScheduleRef, AvailabilityScheduleRule, AvailabilitySelectionValidationParams, AvailabilitySelectionValidationResult, AvailabilitySessionRange, AvailabilityStatus, DateAvailabilityGroup, DateAvailabilityItem, DateAvailabilityQuery, DateAvailabilityQueryResult, GetProductTimeRangesRequest, ProductAvailabilityGroup, ProductAvailabilityItem, ProductAvailabilityQuery, ProductAvailabilityQueryResult, ProductTimeRange, ProductTimeRangeGroup, QueryAvailabilityBaseRequest, ResourceAvailabilityGroup, ResourceAvailabilityQuery, ResourceAvailabilityQueryResult, ResourceAvailabilityRangeGroup, TimeAvailabilityGroup, TimeAvailabilityItem, TimeAvailabilityQuery, TimeAvailabilityQueryResult, } from '../../modules/ResourcePlanner';
|
|
7
11
|
export interface UnifiedBookingSalesState extends BookingTicketState {
|
|
8
12
|
}
|
|
@@ -24,6 +28,12 @@ export declare enum UnifiedBookingSalesHooks {
|
|
|
24
28
|
export interface UnifiedBookingSalesOtherParams extends Record<string, any> {
|
|
25
29
|
enableSessionStoragePersist?: boolean;
|
|
26
30
|
}
|
|
31
|
+
export type UnifiedBookingSalesAllergyRequirement = AllergyRequirement;
|
|
32
|
+
export interface UnifiedBookingSalesResolveProductAllergyRequirementParams {
|
|
33
|
+
productId: number | string;
|
|
34
|
+
productVariantId?: number | string;
|
|
35
|
+
channel?: string;
|
|
36
|
+
}
|
|
27
37
|
export interface UnifiedBookingSalesLoadOpenDataParams {
|
|
28
38
|
businessCode: string;
|
|
29
39
|
channel: string;
|
|
@@ -151,6 +161,8 @@ export interface ContextualCommitAvailabilitySelectionParams {
|
|
|
151
161
|
requirementSelections: AvailabilityRequirementSelection[];
|
|
152
162
|
partySize: number;
|
|
153
163
|
bookingCount?: number;
|
|
164
|
+
/** 新增商品行时与商品、预约在同一次 Order mutation 内写入的 Sales Notes。 */
|
|
165
|
+
salesNotes?: SalesNoteDraftInput[];
|
|
154
166
|
/** 编辑预约时,允许本次显式报价覆盖已落库订单行的历史价格。 */
|
|
155
167
|
allowBookingReprice?: boolean;
|
|
156
168
|
/** 编辑预约重报价后,允许订单模块重新应用已选编辑态折扣。 */
|
|
@@ -205,9 +217,16 @@ export interface UnifiedBookingSalesDiscountResult {
|
|
|
205
217
|
}
|
|
206
218
|
export interface UnifiedBookingSalesApi {
|
|
207
219
|
loadOpenData: (params: UnifiedBookingSalesLoadOpenDataParams) => Promise<OpenDataConfig>;
|
|
220
|
+
/** Returns the latest OpenData payload cached by this solution instance. */
|
|
208
221
|
getOpenData: () => Promise<OpenDataConfig | null>;
|
|
222
|
+
/** Returns the normalized Documents Invoice layout for the active target. */
|
|
223
|
+
getInvoiceLayoutConfig: () => Promise<InvoiceLayoutConfig>;
|
|
224
|
+
/** Returns the Native Invoice template settings for the active target. */
|
|
225
|
+
getInvoiceTemplateSettings: () => Promise<InvoiceTemplateSettings>;
|
|
226
|
+
/** Date-first calendar availability backed by ScheduleModule. */
|
|
209
227
|
loadScheduleAvailableDate: (params: LoadScheduleAvailableDateParams) => Promise<unknown>;
|
|
210
228
|
loadProductsByScheduleDate: (params: UnifiedBookingSalesLoadProductsByScheduleDateParams) => Promise<unknown[]>;
|
|
229
|
+
queryProducts: (params?: ProductListStoreQueryParams) => Promise<ProductData[]>;
|
|
211
230
|
stageRetailProducts: (params: UnifiedBookingSalesStageRetailProductsParams) => Promise<Record<string, any> | null>;
|
|
212
231
|
addRetailProduct: (params: UnifiedBookingSalesAddRetailProductParams) => Promise<Record<string, any>[]>;
|
|
213
232
|
prepareAvailabilityProjection: (params: UnifiedBookingSalesPrepareAvailabilityProjectionParams) => Promise<AvailabilityContextRef>;
|
|
@@ -3140,7 +3140,11 @@ export var VenueBookingImpl = /*#__PURE__*/function (_BaseSalesImpl) {
|
|
|
3140
3140
|
}, {
|
|
3141
3141
|
key: "getItemRuleRuntimeConfig",
|
|
3142
3142
|
value: function getItemRuleRuntimeConfig() {
|
|
3143
|
-
|
|
3143
|
+
var _ref6, _runtimeConfig$channe, _runtimeConfig$custom, _this$otherParams21;
|
|
3144
|
+
var runtimeConfig = this.itemRuleRuntimeConfig || {};
|
|
3145
|
+
return _objectSpread(_objectSpread({}, runtimeConfig), {}, {
|
|
3146
|
+
channel: (_ref6 = (_runtimeConfig$channe = runtimeConfig.channel) !== null && _runtimeConfig$channe !== void 0 ? _runtimeConfig$channe : (_runtimeConfig$custom = runtimeConfig.custom) === null || _runtimeConfig$custom === void 0 ? void 0 : _runtimeConfig$custom.channel) !== null && _ref6 !== void 0 ? _ref6 : (_this$otherParams21 = this.otherParams) === null || _this$otherParams21 === void 0 ? void 0 : _this$otherParams21.channel
|
|
3147
|
+
});
|
|
3144
3148
|
}
|
|
3145
3149
|
}, {
|
|
3146
3150
|
key: "ensureItemRuleConfigsLoaded",
|
|
@@ -3806,14 +3810,14 @@ export var VenueBookingImpl = /*#__PURE__*/function (_BaseSalesImpl) {
|
|
|
3806
3810
|
key: "setOtherParams",
|
|
3807
3811
|
value: function () {
|
|
3808
3812
|
var _setOtherParams = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee46(params) {
|
|
3809
|
-
var
|
|
3810
|
-
|
|
3813
|
+
var _ref8,
|
|
3814
|
+
_ref8$cover,
|
|
3811
3815
|
cover,
|
|
3812
3816
|
_args46 = arguments;
|
|
3813
3817
|
return _regeneratorRuntime().wrap(function _callee46$(_context46) {
|
|
3814
3818
|
while (1) switch (_context46.prev = _context46.next) {
|
|
3815
3819
|
case 0:
|
|
3816
|
-
|
|
3820
|
+
_ref8 = _args46.length > 1 && _args46[1] !== undefined ? _args46[1] : {}, _ref8$cover = _ref8.cover, cover = _ref8$cover === void 0 ? false : _ref8$cover;
|
|
3817
3821
|
if (cover) {
|
|
3818
3822
|
this.otherParams = params;
|
|
3819
3823
|
} else {
|
|
@@ -3,7 +3,7 @@ import type { ConditionScope, DataVariantBusinessData, DataVariantMenu, DataVari
|
|
|
3
3
|
/**
|
|
4
4
|
* Data Variant 评估器。
|
|
5
5
|
*
|
|
6
|
-
*
|
|
6
|
+
* OS Server 商品路由开启时由 ProductsModule 调用;路由关闭时由 ProductList 在远端响应后调用。
|
|
7
7
|
*/
|
|
8
8
|
export declare class DataVariantEvaluator {
|
|
9
9
|
private adapter;
|
|
@@ -17,7 +17,7 @@ export declare class DataVariantEvaluator {
|
|
|
17
17
|
/**
|
|
18
18
|
* 批量解析商品 Data Variant。
|
|
19
19
|
*
|
|
20
|
-
*
|
|
20
|
+
* 优先使用商品 metadata.data_variant_ids;缺少权威命中结果时才执行本地策略判断。
|
|
21
21
|
*/
|
|
22
22
|
resolveProducts(businessData: DataVariantBusinessData): ResolvedDataVariantResult;
|
|
23
23
|
/**
|
|
@@ -34,8 +34,24 @@ export declare class DataVariantEvaluator {
|
|
|
34
34
|
* 这里不依赖餐牌,只读取策略配置和调用方传入的 scheduleList。
|
|
35
35
|
*/
|
|
36
36
|
buildScheduleTimePoints(strategyConfigs: StrategyConfig<DataVariantRuleMetadata>[], scheduleList: NonNullable<DataVariantBusinessData['scheduleList']>): string[];
|
|
37
|
+
/**
|
|
38
|
+
* 判断商品(含套餐子商品)是否仍需要本地执行策略条件。
|
|
39
|
+
* metadata.data_variant_ids 只要是数组,即使为空,也表示接口已经给出权威命中结果。
|
|
40
|
+
*/
|
|
41
|
+
private requiresLocalStrategyEvaluation;
|
|
42
|
+
/**
|
|
43
|
+
* 将接口返回的高 -> 低优先级 Variant ID 转成数值优先级。
|
|
44
|
+
* 返回 null 表示接口没有提供该字段;空 Map 表示已计算但没有任何命中。
|
|
45
|
+
*/
|
|
46
|
+
private getAuthoritativeVariantPriorities;
|
|
47
|
+
private normalizeDataVariantId;
|
|
37
48
|
private buildGlobalEffectiveRuleMap;
|
|
38
49
|
private resolveProduct;
|
|
50
|
+
/**
|
|
51
|
+
* 多货币 Data Variant 只生成候选列表,不参与商品字段覆盖。
|
|
52
|
+
* rule_id=0 是无条件默认价;同币种命中的条件价优先于默认价。
|
|
53
|
+
*/
|
|
54
|
+
private resolveSettlementOptions;
|
|
39
55
|
/**
|
|
40
56
|
* 解析套餐子商品上的 Data Variant 覆盖。
|
|
41
57
|
*
|
|
@@ -10,7 +10,7 @@ var _adapter = require("./adapter");
|
|
|
10
10
|
/**
|
|
11
11
|
* Data Variant 评估器。
|
|
12
12
|
*
|
|
13
|
-
*
|
|
13
|
+
* OS Server 商品路由开启时由 ProductsModule 调用;路由关闭时由 ProductList 在远端响应后调用。
|
|
14
14
|
*/
|
|
15
15
|
class DataVariantEvaluator {
|
|
16
16
|
adapter;
|
|
@@ -32,16 +32,22 @@ class DataVariantEvaluator {
|
|
|
32
32
|
/**
|
|
33
33
|
* 批量解析商品 Data Variant。
|
|
34
34
|
*
|
|
35
|
-
*
|
|
35
|
+
* 优先使用商品 metadata.data_variant_ids;缺少权威命中结果时才执行本地策略判断。
|
|
36
36
|
*/
|
|
37
37
|
resolveProducts(businessData) {
|
|
38
38
|
const strategyConfigs = businessData.strategyConfigs ? this.normalizeStrategyConfigs(businessData.strategyConfigs) : this.strategyConfigs;
|
|
39
39
|
const menuScopeMap = this.buildMenuScopeMap(businessData.menuList || []);
|
|
40
40
|
const scheduleTimePoints = this.buildScheduleTimePoints(strategyConfigs, businessData.scheduleList || []);
|
|
41
|
+
// metadata.data_variant_ids 存在时,接口已经完成规则计算;只有仍有商品缺少
|
|
42
|
+
// 该权威结果时才初始化本地策略引擎,避免 OS/接口与客户端重复评估。
|
|
43
|
+
const shouldEvaluateStrategies = businessData.products.some(product => this.requiresLocalStrategyEvaluation(product));
|
|
41
44
|
const {
|
|
42
45
|
globalEffectiveRuleMap,
|
|
43
46
|
rawResults
|
|
44
|
-
} = this.buildGlobalEffectiveRuleMap(strategyConfigs, businessData, menuScopeMap)
|
|
47
|
+
} = shouldEvaluateStrategies ? this.buildGlobalEffectiveRuleMap(strategyConfigs, businessData, menuScopeMap) : {
|
|
48
|
+
globalEffectiveRuleMap: new Map(),
|
|
49
|
+
rawResults: []
|
|
50
|
+
};
|
|
45
51
|
const products = businessData.products.map(product => {
|
|
46
52
|
const resolvedProduct = this.resolveProduct(product, businessData, globalEffectiveRuleMap, menuScopeMap);
|
|
47
53
|
// 套餐子商品也可能携带 data_variants,需在同一策略上下文内继续评估并写回 bundle_item。
|
|
@@ -124,6 +130,46 @@ class DataVariantEvaluator {
|
|
|
124
130
|
const matchedSchedules = scheduleList.filter(schedule => scheduleIdSet.has(Number(schedule.id)));
|
|
125
131
|
return this.extractTimePointsFromSchedules(matchedSchedules);
|
|
126
132
|
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* 判断商品(含套餐子商品)是否仍需要本地执行策略条件。
|
|
136
|
+
* metadata.data_variant_ids 只要是数组,即使为空,也表示接口已经给出权威命中结果。
|
|
137
|
+
*/
|
|
138
|
+
requiresLocalStrategyEvaluation(product) {
|
|
139
|
+
const dataVariants = this.getProductDataVariants(product);
|
|
140
|
+
const hasConditionalVariant = dataVariants.some(variant => Number(variant.data_variant_rule_id) !== 0);
|
|
141
|
+
if (hasConditionalVariant && this.getAuthoritativeVariantPriorities(product) === null) {
|
|
142
|
+
return true;
|
|
143
|
+
}
|
|
144
|
+
const bundleGroups = Array.isArray(product.bundle_group) ? product.bundle_group : [];
|
|
145
|
+
return bundleGroups.some(group => {
|
|
146
|
+
const bundleItems = Array.isArray(group?.bundle_item) ? group.bundle_item : [];
|
|
147
|
+
return bundleItems.some(item => this.requiresLocalStrategyEvaluation(this.toBundleItemDataVariantProduct(item)));
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* 将接口返回的高 -> 低优先级 Variant ID 转成数值优先级。
|
|
153
|
+
* 返回 null 表示接口没有提供该字段;空 Map 表示已计算但没有任何命中。
|
|
154
|
+
*/
|
|
155
|
+
getAuthoritativeVariantPriorities(product) {
|
|
156
|
+
const rawIds = product?.metadata?.data_variant_ids;
|
|
157
|
+
if (!Array.isArray(rawIds)) return null;
|
|
158
|
+
const orderedIds = [];
|
|
159
|
+
const seen = new Set();
|
|
160
|
+
for (const rawId of rawIds) {
|
|
161
|
+
const id = this.normalizeDataVariantId(rawId);
|
|
162
|
+
if (id === null || seen.has(id)) continue;
|
|
163
|
+
seen.add(id);
|
|
164
|
+
orderedIds.push(id);
|
|
165
|
+
}
|
|
166
|
+
return new Map(orderedIds.map((id, index) => [id, orderedIds.length - index]));
|
|
167
|
+
}
|
|
168
|
+
normalizeDataVariantId(value) {
|
|
169
|
+
if (value === undefined || value === null) return null;
|
|
170
|
+
const normalized = String(value).trim();
|
|
171
|
+
return normalized || null;
|
|
172
|
+
}
|
|
127
173
|
buildGlobalEffectiveRuleMap(strategyConfigs, businessData, menuScopeMap) {
|
|
128
174
|
const globalEffectiveRuleMap = new Map();
|
|
129
175
|
const rawResults = [];
|
|
@@ -155,7 +201,22 @@ class DataVariantEvaluator {
|
|
|
155
201
|
const dataVariants = this.getProductDataVariants(product);
|
|
156
202
|
if (!dataVariants.length) return product;
|
|
157
203
|
const matchedVariants = [];
|
|
204
|
+
const authoritativeVariantPriorities = this.getAuthoritativeVariantPriorities(product);
|
|
205
|
+
const settlementVariants = dataVariants.filter(variant => variant.business_type === 'multi_currency_settlement' && variant.status === 'enable');
|
|
158
206
|
for (const variant of dataVariants) {
|
|
207
|
+
if (variant.business_type === 'multi_currency_settlement') continue;
|
|
208
|
+
if (authoritativeVariantPriorities !== null) {
|
|
209
|
+
const variantId = this.normalizeDataVariantId(variant.id);
|
|
210
|
+
const priority = variantId === null ? undefined : authoritativeVariantPriorities.get(variantId);
|
|
211
|
+
if (priority === undefined) continue;
|
|
212
|
+
matchedVariants.push({
|
|
213
|
+
ruleId: String(variant.data_variant_rule_id),
|
|
214
|
+
priority,
|
|
215
|
+
data: variant.data || {},
|
|
216
|
+
variant
|
|
217
|
+
});
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
159
220
|
const rule = globalEffectiveRuleMap.get(String(variant.data_variant_rule_id));
|
|
160
221
|
if (!rule) continue;
|
|
161
222
|
if (!this.evaluateProductRules(rule, product, businessData, menuScopeMap)) {
|
|
@@ -168,8 +229,76 @@ class DataVariantEvaluator {
|
|
|
168
229
|
variant
|
|
169
230
|
});
|
|
170
231
|
}
|
|
171
|
-
|
|
172
|
-
|
|
232
|
+
const resolvedProduct = matchedVariants.length ? this.mergeMatchedVariants(product, matchedVariants) : product;
|
|
233
|
+
const availableSettlementOptions = this.resolveSettlementOptions(settlementVariants, product, businessData, globalEffectiveRuleMap, menuScopeMap, authoritativeVariantPriorities);
|
|
234
|
+
if (!settlementVariants.length) return resolvedProduct;
|
|
235
|
+
return {
|
|
236
|
+
...resolvedProduct,
|
|
237
|
+
available_settlement_options: availableSettlementOptions
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* 多货币 Data Variant 只生成候选列表,不参与商品字段覆盖。
|
|
243
|
+
* rule_id=0 是无条件默认价;同币种命中的条件价优先于默认价。
|
|
244
|
+
*/
|
|
245
|
+
resolveSettlementOptions(variants, product, businessData, globalEffectiveRuleMap, menuScopeMap, authoritativeVariantPriorities) {
|
|
246
|
+
if (!variants.length) return [];
|
|
247
|
+
const candidates = variants.flatMap(variant => {
|
|
248
|
+
const settlement = variant.data?.multi_currency_settlement;
|
|
249
|
+
if (!settlement || typeof settlement !== 'object') return [];
|
|
250
|
+
const ruleId = String(variant.data_variant_rule_id);
|
|
251
|
+
const isDefault = Number(variant.data_variant_rule_id) === 0;
|
|
252
|
+
const rule = isDefault ? undefined : globalEffectiveRuleMap.get(ruleId);
|
|
253
|
+
const variantId = this.normalizeDataVariantId(variant.id);
|
|
254
|
+
const authoritativePriority = variantId === null ? undefined : authoritativeVariantPriorities?.get(variantId);
|
|
255
|
+
let available;
|
|
256
|
+
let priority;
|
|
257
|
+
if (isDefault) {
|
|
258
|
+
// rule 0 是无条件默认候选,不依赖接口命中 ID 或本地策略引擎。
|
|
259
|
+
available = true;
|
|
260
|
+
priority = Number.NEGATIVE_INFINITY;
|
|
261
|
+
} else if (authoritativeVariantPriorities !== null) {
|
|
262
|
+
// 数组存在(包括 [])即表示接口已经算过规则,不再在客户端二次判断。
|
|
263
|
+
available = authoritativePriority !== undefined;
|
|
264
|
+
priority = authoritativePriority ?? rule?.priority ?? Number.NEGATIVE_INFINITY;
|
|
265
|
+
} else {
|
|
266
|
+
available = Boolean(rule && this.evaluateProductRules(rule, product, businessData, menuScopeMap));
|
|
267
|
+
priority = rule?.priority ?? Number.NEGATIVE_INFINITY;
|
|
268
|
+
}
|
|
269
|
+
const settlementType = String(settlement.settlement_type || '');
|
|
270
|
+
const currencyProductId = this.toNumber(settlement.currency_product_id);
|
|
271
|
+
const settlementKey = settlementType === 'virtual_currency' ? `virtual_currency:${currencyProductId || ''}` : settlementType || `variant:${variant.id ?? ruleId}`;
|
|
272
|
+
return [{
|
|
273
|
+
key: settlementKey,
|
|
274
|
+
priority,
|
|
275
|
+
isDefault,
|
|
276
|
+
option: {
|
|
277
|
+
...settlement,
|
|
278
|
+
settlement_type: settlementType,
|
|
279
|
+
...(currencyProductId ? {
|
|
280
|
+
currency_product_id: currencyProductId
|
|
281
|
+
} : {}),
|
|
282
|
+
price: String(variant.data?.price ?? '0'),
|
|
283
|
+
data_variant_id: variant.id,
|
|
284
|
+
data_variant_rule_id: variant.data_variant_rule_id,
|
|
285
|
+
available,
|
|
286
|
+
disabled_reason: available ? '' : String(settlement.disabled_reason || 'Not available for this purchase.')
|
|
287
|
+
}
|
|
288
|
+
}];
|
|
289
|
+
});
|
|
290
|
+
const grouped = new Map();
|
|
291
|
+
for (const candidate of candidates) {
|
|
292
|
+
const group = grouped.get(candidate.key) || [];
|
|
293
|
+
group.push(candidate);
|
|
294
|
+
grouped.set(candidate.key, group);
|
|
295
|
+
}
|
|
296
|
+
return Array.from(grouped.values()).map(group => {
|
|
297
|
+
const matchedConditional = group.filter(candidate => !candidate.isDefault && candidate.option.available).sort((a, b) => b.priority - a.priority)[0];
|
|
298
|
+
const defaultCandidate = group.find(candidate => candidate.isDefault);
|
|
299
|
+
const unavailableCandidate = [...group].filter(candidate => !candidate.option.available).sort((a, b) => b.priority - a.priority)[0];
|
|
300
|
+
return (matchedConditional || defaultCandidate || unavailableCandidate).option;
|
|
301
|
+
});
|
|
173
302
|
}
|
|
174
303
|
|
|
175
304
|
/**
|
|
@@ -222,7 +351,8 @@ class DataVariantEvaluator {
|
|
|
222
351
|
product_variant_id: variantId,
|
|
223
352
|
variant_id: variantId,
|
|
224
353
|
collection: item.collection,
|
|
225
|
-
data_variants: item.data_variants
|
|
354
|
+
data_variants: item.data_variants,
|
|
355
|
+
metadata: item.metadata
|
|
226
356
|
};
|
|
227
357
|
}
|
|
228
358
|
|
|
@@ -453,6 +583,7 @@ class DataVariantEvaluator {
|
|
|
453
583
|
}
|
|
454
584
|
result._dataVariant = {
|
|
455
585
|
appliedRuleIds,
|
|
586
|
+
appliedVariantIds: Array.from(new Set([...sortedVariants].reverse().map(matched => this.normalizeDataVariantId(matched.variant.id)).filter(id => id !== null))),
|
|
456
587
|
appliedFields: Array.from(appliedFields),
|
|
457
588
|
original
|
|
458
589
|
};
|
|
@@ -35,6 +35,10 @@ export interface DataVariantItem {
|
|
|
35
35
|
module_data_id?: number;
|
|
36
36
|
/** 关联策略模型 metadata.id / data_variant_rule.id */
|
|
37
37
|
data_variant_rule_id: number | string;
|
|
38
|
+
/** 业务类型;多货币结算候选不会走普通字段覆盖 */
|
|
39
|
+
business_type?: 'multi_currency_settlement' | string;
|
|
40
|
+
/** 数据状态;多货币结算仅处理 enable 记录 */
|
|
41
|
+
status?: 'enable' | 'disable' | string;
|
|
38
42
|
/** 命中策略后需要覆盖到商品上的字段 */
|
|
39
43
|
data: Record<string, any>;
|
|
40
44
|
created_at?: string | null;
|
|
@@ -53,11 +57,30 @@ export interface DataVariantProduct {
|
|
|
53
57
|
id?: number | string;
|
|
54
58
|
[key: string]: any;
|
|
55
59
|
}>;
|
|
60
|
+
/**
|
|
61
|
+
* 商品接口已经完成规则计算时返回的命中结果。
|
|
62
|
+
* 数组按 Data Variant 优先级从高到低排列;空数组表示已计算但无命中。
|
|
63
|
+
*/
|
|
64
|
+
metadata?: {
|
|
65
|
+
data_variant_ids?: Array<number | string>;
|
|
66
|
+
[key: string]: any;
|
|
67
|
+
};
|
|
56
68
|
/** 后端 /product/query with dataVariants 返回的智能定价变体列表 */
|
|
57
69
|
data_variants?: DataVariantItem[];
|
|
70
|
+
/** 当前上下文解析后的可选结算货币,不覆盖商品原始多货币配置 */
|
|
71
|
+
available_settlement_options?: AvailableSettlementOption[];
|
|
58
72
|
_dataVariant?: DataVariantAppliedInfo;
|
|
59
73
|
[key: string]: any;
|
|
60
74
|
}
|
|
75
|
+
export interface AvailableSettlementOption extends Record<string, any> {
|
|
76
|
+
settlement_type: 'legal_currency' | 'virtual_currency' | string;
|
|
77
|
+
currency_product_id?: number;
|
|
78
|
+
price: string;
|
|
79
|
+
data_variant_id?: number;
|
|
80
|
+
data_variant_rule_id: number | string;
|
|
81
|
+
available: boolean;
|
|
82
|
+
disabled_reason?: string;
|
|
83
|
+
}
|
|
61
84
|
/**
|
|
62
85
|
* 餐牌商品配置项。
|
|
63
86
|
*/
|
|
@@ -131,6 +154,8 @@ export interface MatchedDataVariant {
|
|
|
131
154
|
*/
|
|
132
155
|
export interface DataVariantAppliedInfo {
|
|
133
156
|
appliedRuleIds: string[];
|
|
157
|
+
/** 本地策略实际应用的 Data Variant ID,按优先级从高到低排列。 */
|
|
158
|
+
appliedVariantIds?: Array<number | string>;
|
|
134
159
|
appliedFields: string[];
|
|
135
160
|
original: DataVariantProduct;
|
|
136
161
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { EvaluationResult, RuntimeContext, ActionEffect } from '../../type';
|
|
2
2
|
import type { BusinessAdapter } from '../type';
|
|
3
|
-
import { type ItemRuleBusinessData, type QuantityCheckResult, type PrefillItemResult, type QuantityLimitResult } from './type';
|
|
3
|
+
import { type ItemRuleBusinessData, type QuantityCheckResult, type PrefillItemResult, type QuantityLimitResult, type AllergyRequirement } from './type';
|
|
4
4
|
/**
|
|
5
5
|
* ItemRule 适配器
|
|
6
6
|
*
|
|
@@ -14,7 +14,10 @@ export declare class ItemRuleAdapter implements BusinessAdapter {
|
|
|
14
14
|
transformResult(result: EvaluationResult, businessData?: ItemRuleBusinessData): {
|
|
15
15
|
quantityChecks: QuantityCheckResult[];
|
|
16
16
|
prefillItems: PrefillItemResult[];
|
|
17
|
+
allergyRequirements: AllergyRequirement[];
|
|
17
18
|
};
|
|
19
|
+
private processAllergyCheck;
|
|
20
|
+
private normalizeAllergyDataSourceItems;
|
|
18
21
|
formatConfig(result: EvaluationResult, businessData?: ItemRuleBusinessData): {
|
|
19
22
|
result: EvaluationResult;
|
|
20
23
|
businessData?: ItemRuleBusinessData;
|
|
@@ -19,14 +19,17 @@ class ItemRuleAdapter {
|
|
|
19
19
|
pax,
|
|
20
20
|
cartItems,
|
|
21
21
|
serviceType,
|
|
22
|
+
channel,
|
|
22
23
|
submissionIndex,
|
|
23
24
|
custom
|
|
24
25
|
} = businessData;
|
|
26
|
+
const resolvedChannel = String(channel ?? custom?.channel ?? '').trim();
|
|
25
27
|
return {
|
|
26
28
|
entities: {
|
|
27
29
|
cartItems,
|
|
28
30
|
historicalItems: businessData.historicalItems || [],
|
|
29
|
-
pax
|
|
31
|
+
pax,
|
|
32
|
+
currentProduct: businessData.currentProduct
|
|
30
33
|
},
|
|
31
34
|
attributes: {
|
|
32
35
|
serviceType: serviceType || '',
|
|
@@ -36,7 +39,8 @@ class ItemRuleAdapter {
|
|
|
36
39
|
submissionIndex: submissionIndex ?? 0,
|
|
37
40
|
cartItemCount: cartItems.length,
|
|
38
41
|
cartTotalQuantity: cartItems.reduce((sum, item) => sum + item.quantity, 0),
|
|
39
|
-
...custom
|
|
42
|
+
...custom,
|
|
43
|
+
channel: resolvedChannel
|
|
40
44
|
},
|
|
41
45
|
metadata: {
|
|
42
46
|
timestamp: Date.now()
|
|
@@ -46,10 +50,12 @@ class ItemRuleAdapter {
|
|
|
46
50
|
transformResult(result, businessData) {
|
|
47
51
|
const quantityChecks = [];
|
|
48
52
|
const prefillItems = [];
|
|
53
|
+
const allergyRequirements = [];
|
|
49
54
|
if (!result.applicable || !businessData) {
|
|
50
55
|
return {
|
|
51
56
|
quantityChecks,
|
|
52
|
-
prefillItems
|
|
57
|
+
prefillItems,
|
|
58
|
+
allergyRequirements
|
|
53
59
|
};
|
|
54
60
|
}
|
|
55
61
|
for (const action of result.matchedActions) {
|
|
@@ -61,13 +67,76 @@ class ItemRuleAdapter {
|
|
|
61
67
|
} else if (action.type === _type.ITEM_RULE_ACTION_TYPES.PREFILL_CART) {
|
|
62
68
|
const items = this.processPrefillCart(action, businessData);
|
|
63
69
|
prefillItems.push(...items);
|
|
70
|
+
} else if (action.type === _type.ITEM_RULE_ACTION_TYPES.ALLERGY_CHECK) {
|
|
71
|
+
const requirement = this.processAllergyCheck(action, result, businessData);
|
|
72
|
+
if (requirement) allergyRequirements.push(requirement);
|
|
64
73
|
}
|
|
65
74
|
}
|
|
66
75
|
return {
|
|
67
76
|
quantityChecks,
|
|
68
|
-
prefillItems
|
|
77
|
+
prefillItems,
|
|
78
|
+
allergyRequirements
|
|
69
79
|
};
|
|
70
80
|
}
|
|
81
|
+
|
|
82
|
+
// ============================================
|
|
83
|
+
// Allergy Check 处理
|
|
84
|
+
// ============================================
|
|
85
|
+
|
|
86
|
+
processAllergyCheck(action, result, businessData) {
|
|
87
|
+
const config = action.config;
|
|
88
|
+
const currentProduct = businessData.currentProduct;
|
|
89
|
+
if (!config || config.targetType !== 'product' || config.promptScope !== 'once_per_order_product' || !Array.isArray(config.targets) || !currentProduct) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
const currentProductId = Number(currentProduct.product_id);
|
|
93
|
+
const currentVariantId = Number(currentProduct.product_variant_id ?? 0);
|
|
94
|
+
if (!Number.isFinite(currentProductId) || currentProductId <= 0) return null;
|
|
95
|
+
const productTargets = config.targets.filter(item => Number(item.product_id) === currentProductId);
|
|
96
|
+
const target = productTargets.find(item => currentVariantId > 0 && Number(item.product_variant_id ?? 0) === currentVariantId) ?? productTargets.find(item => Number(item.product_variant_id ?? 0) <= 0);
|
|
97
|
+
if (!target || target.dataSource?.type !== 'inline') return null;
|
|
98
|
+
const items = this.normalizeAllergyDataSourceItems(target.dataSource.items);
|
|
99
|
+
if (!items.length) return null;
|
|
100
|
+
return {
|
|
101
|
+
strategyId: result.config.metadata.id,
|
|
102
|
+
ruleId: action.id,
|
|
103
|
+
priority: action.priority ?? 0,
|
|
104
|
+
targetType: 'product',
|
|
105
|
+
product_id: currentProductId,
|
|
106
|
+
...(Number(target.product_variant_id ?? 0) > 0 ? {
|
|
107
|
+
product_variant_id: Number(target.product_variant_id)
|
|
108
|
+
} : {}),
|
|
109
|
+
dataSource: {
|
|
110
|
+
type: 'inline',
|
|
111
|
+
items
|
|
112
|
+
},
|
|
113
|
+
promptScope: config.promptScope,
|
|
114
|
+
allowNote: config.allowNote === true
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
normalizeAllergyDataSourceItems(items) {
|
|
118
|
+
if (!Array.isArray(items)) return [];
|
|
119
|
+
const uniqueIds = new Set();
|
|
120
|
+
const normalized = [];
|
|
121
|
+
for (const item of items) {
|
|
122
|
+
const id = String(item?.id ?? '').trim();
|
|
123
|
+
const rawLabel = item?.label;
|
|
124
|
+
const label = typeof rawLabel === 'string' ? rawLabel.trim() : Object.entries(rawLabel || {}).reduce((labels, [locale, value]) => {
|
|
125
|
+
if (typeof value === 'string' && value.trim().length > 0) {
|
|
126
|
+
labels[locale] = value.trim();
|
|
127
|
+
}
|
|
128
|
+
return labels;
|
|
129
|
+
}, {});
|
|
130
|
+
const hasLabel = typeof label === 'string' ? label.length > 0 : Object.keys(label).length > 0;
|
|
131
|
+
if (!id || !hasLabel || uniqueIds.has(id)) continue;
|
|
132
|
+
uniqueIds.add(id);
|
|
133
|
+
normalized.push({
|
|
134
|
+
id,
|
|
135
|
+
label
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
return normalized;
|
|
139
|
+
}
|
|
71
140
|
formatConfig(result, businessData) {
|
|
72
141
|
return {
|
|
73
142
|
result,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { StrategyConfig } from '../../type';
|
|
2
|
-
import { type ItemRuleBusinessData, type ItemRuleEvaluationResult, type QuantityCheckResult, type QuantityLimitResult, type PrefillItemResult } from './type';
|
|
2
|
+
import { type ItemRuleBusinessData, type ItemRuleEvaluationResult, type QuantityCheckResult, type QuantityLimitResult, type PrefillItemResult, type AllergyRequirement } from './type';
|
|
3
3
|
/**
|
|
4
4
|
* ItemRule 评估器
|
|
5
5
|
*
|
|
@@ -21,6 +21,13 @@ export declare class ItemRuleEvaluator {
|
|
|
21
21
|
* 完整评估:数量校验 + 预填购物车
|
|
22
22
|
*/
|
|
23
23
|
evaluate(businessData: ItemRuleBusinessData): ItemRuleEvaluationResult;
|
|
24
|
+
/**
|
|
25
|
+
* 获取当前商品优先级最高的过敏门禁要求。
|
|
26
|
+
*
|
|
27
|
+
* 不同商品或不同 Action 的数据源不会合并;相同优先级按
|
|
28
|
+
* strategyId + ruleId 稳定排序,保证多次评估结果一致。
|
|
29
|
+
*/
|
|
30
|
+
getAllergyRequirement(businessData: ItemRuleBusinessData): AllergyRequirement | null;
|
|
24
31
|
/**
|
|
25
32
|
* 获取当前配置中各商品的数量限制
|
|
26
33
|
*
|
|
@@ -43,6 +43,7 @@ class ItemRuleEvaluator {
|
|
|
43
43
|
const configs = businessData.strategyConfigs ? businessData.strategyConfigs.filter(c => c.metadata.type === 'item_rule') : this.strategyConfigs;
|
|
44
44
|
const allQuantityChecks = [];
|
|
45
45
|
const allPrefillItems = [];
|
|
46
|
+
const allAllergyRequirements = [];
|
|
46
47
|
const rawResults = [];
|
|
47
48
|
const context = this.adapter.prepareContext(businessData);
|
|
48
49
|
for (const config of configs) {
|
|
@@ -50,21 +51,42 @@ class ItemRuleEvaluator {
|
|
|
50
51
|
rawResults.push(evalResult);
|
|
51
52
|
const {
|
|
52
53
|
quantityChecks,
|
|
53
|
-
prefillItems
|
|
54
|
+
prefillItems,
|
|
55
|
+
allergyRequirements
|
|
54
56
|
} = this.adapter.transformResult(evalResult, businessData);
|
|
55
57
|
allQuantityChecks.push(...quantityChecks);
|
|
56
58
|
allPrefillItems.push(...prefillItems);
|
|
59
|
+
allAllergyRequirements.push(...allergyRequirements);
|
|
57
60
|
}
|
|
58
61
|
const failedChecks = allQuantityChecks.filter(c => !c.passed);
|
|
59
62
|
return {
|
|
60
63
|
quantityChecks: allQuantityChecks,
|
|
61
64
|
prefillItems: allPrefillItems,
|
|
65
|
+
allergyRequirements: allAllergyRequirements,
|
|
62
66
|
allPassed: failedChecks.length === 0,
|
|
63
67
|
failedChecks,
|
|
64
68
|
rawResults
|
|
65
69
|
};
|
|
66
70
|
}
|
|
67
71
|
|
|
72
|
+
/**
|
|
73
|
+
* 获取当前商品优先级最高的过敏门禁要求。
|
|
74
|
+
*
|
|
75
|
+
* 不同商品或不同 Action 的数据源不会合并;相同优先级按
|
|
76
|
+
* strategyId + ruleId 稳定排序,保证多次评估结果一致。
|
|
77
|
+
*/
|
|
78
|
+
getAllergyRequirement(businessData) {
|
|
79
|
+
const requirements = this.evaluate(businessData).allergyRequirements;
|
|
80
|
+
if (!requirements.length) return null;
|
|
81
|
+
return [...requirements].sort((left, right) => {
|
|
82
|
+
const priorityDiff = right.priority - left.priority;
|
|
83
|
+
if (priorityDiff !== 0) return priorityDiff;
|
|
84
|
+
const strategyDiff = left.strategyId.localeCompare(right.strategyId);
|
|
85
|
+
if (strategyDiff !== 0) return strategyDiff;
|
|
86
|
+
return left.ruleId.localeCompare(right.ruleId);
|
|
87
|
+
})[0];
|
|
88
|
+
}
|
|
89
|
+
|
|
68
90
|
/**
|
|
69
91
|
* 获取当前配置中各商品的数量限制
|
|
70
92
|
*
|