@pisell/pisellos 2.2.71 → 2.2.72
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/index.d.ts +2 -0
- package/dist/model/strategy/adapter/index.js +2 -1
- package/dist/model/strategy/adapter/promotion/adapter.d.ts +66 -0
- package/dist/model/strategy/adapter/promotion/adapter.js +271 -0
- package/dist/model/strategy/adapter/promotion/evaluator.d.ts +213 -0
- package/dist/model/strategy/adapter/promotion/evaluator.js +1206 -0
- package/dist/model/strategy/adapter/promotion/examples.d.ts +138 -0
- package/dist/model/strategy/adapter/promotion/examples.js +166 -0
- package/dist/model/strategy/adapter/promotion/index.d.ts +4 -0
- package/dist/model/strategy/adapter/promotion/index.js +0 -0
- package/dist/model/strategy/adapter/promotion/type.d.ts +447 -0
- package/dist/model/strategy/adapter/promotion/type.js +209 -0
- package/dist/model/strategy/adapter/walletPass/evaluator.js +4 -1
- package/dist/model/strategy/engine.d.ts +106 -0
- package/dist/model/strategy/engine.js +611 -0
- package/dist/model/strategy/index.d.ts +2 -93
- package/dist/model/strategy/index.js +6 -549
- package/dist/modules/BaseModule.d.ts +4 -0
- package/dist/modules/BaseModule.js +5 -0
- package/dist/modules/Rules/index.d.ts +1 -0
- package/dist/modules/Rules/index.js +28 -16
- package/lib/model/strategy/adapter/index.d.ts +2 -0
- package/lib/model/strategy/adapter/index.js +6 -0
- package/lib/model/strategy/adapter/promotion/adapter.d.ts +66 -0
- package/lib/model/strategy/adapter/promotion/adapter.js +217 -0
- package/lib/model/strategy/adapter/promotion/evaluator.d.ts +213 -0
- package/lib/model/strategy/adapter/promotion/evaluator.js +844 -0
- package/lib/model/strategy/adapter/promotion/examples.d.ts +138 -0
- package/lib/model/strategy/adapter/promotion/examples.js +192 -0
- package/lib/model/strategy/adapter/promotion/index.d.ts +4 -0
- package/lib/model/strategy/adapter/promotion/index.js +0 -0
- package/lib/model/strategy/adapter/promotion/type.d.ts +447 -0
- package/lib/model/strategy/adapter/promotion/type.js +51 -0
- package/lib/model/strategy/adapter/walletPass/evaluator.js +2 -1
- package/lib/model/strategy/engine.d.ts +106 -0
- package/lib/model/strategy/engine.js +450 -0
- package/lib/model/strategy/index.d.ts +2 -93
- package/lib/model/strategy/index.js +6 -381
- package/lib/modules/BaseModule.d.ts +4 -0
- package/lib/modules/BaseModule.js +3 -0
- package/lib/modules/Rules/index.d.ts +1 -0
- package/lib/modules/Rules/index.js +23 -17
- package/package.json +1 -1
|
@@ -0,0 +1,844 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
20
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
21
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
22
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
23
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
24
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
25
|
+
mod
|
|
26
|
+
));
|
|
27
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
28
|
+
|
|
29
|
+
// src/model/strategy/adapter/promotion/evaluator.ts
|
|
30
|
+
var evaluator_exports = {};
|
|
31
|
+
__export(evaluator_exports, {
|
|
32
|
+
PromotionEvaluator: () => PromotionEvaluator
|
|
33
|
+
});
|
|
34
|
+
module.exports = __toCommonJS(evaluator_exports);
|
|
35
|
+
var import_decimal = __toESM(require("decimal.js"));
|
|
36
|
+
var import_engine = require("../../engine");
|
|
37
|
+
var import_adapter = require("./adapter");
|
|
38
|
+
var import_type = require("./type");
|
|
39
|
+
var defaultLocales = {
|
|
40
|
+
en: {
|
|
41
|
+
no_applicable_promotion: "No applicable promotion",
|
|
42
|
+
promotion_not_in_time_range: "Promotion not in valid time range",
|
|
43
|
+
product_not_in_promotion: "Product not in promotion scope"
|
|
44
|
+
},
|
|
45
|
+
"zh-CN": {
|
|
46
|
+
no_applicable_promotion: "无适用的促销活动",
|
|
47
|
+
promotion_not_in_time_range: "促销活动不在有效时间范围内",
|
|
48
|
+
product_not_in_promotion: "商品不在促销范围内"
|
|
49
|
+
},
|
|
50
|
+
"zh-HK": {
|
|
51
|
+
no_applicable_promotion: "無適用的促銷活動",
|
|
52
|
+
promotion_not_in_time_range: "促銷活動不在有效時間範圍內",
|
|
53
|
+
product_not_in_promotion: "商品不在促銷範圍內"
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
var PromotionEvaluator = class {
|
|
57
|
+
constructor() {
|
|
58
|
+
this.strategyConfigs = [];
|
|
59
|
+
this.locale = "en";
|
|
60
|
+
this.locales = defaultLocales;
|
|
61
|
+
this.engine = new import_engine.StrategyEngine({
|
|
62
|
+
debug: false,
|
|
63
|
+
enableTrace: false
|
|
64
|
+
});
|
|
65
|
+
this.adapter = new import_adapter.PromotionAdapter();
|
|
66
|
+
}
|
|
67
|
+
// ============================================
|
|
68
|
+
// 配置管理
|
|
69
|
+
// ============================================
|
|
70
|
+
/**
|
|
71
|
+
* 设置策略配置列表
|
|
72
|
+
*/
|
|
73
|
+
setStrategyConfigs(strategyConfigs) {
|
|
74
|
+
const newStrategyConfigs = strategyConfigs.filter((item) => item.metadata.type === "promotion");
|
|
75
|
+
this.strategyConfigs = newStrategyConfigs;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* 获取策略配置列表
|
|
79
|
+
*/
|
|
80
|
+
getStrategyConfigs() {
|
|
81
|
+
return this.strategyConfigs;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* 添加策略配置
|
|
85
|
+
*/
|
|
86
|
+
addStrategyConfig(strategyConfig) {
|
|
87
|
+
this.strategyConfigs.push(strategyConfig);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* 设置语言
|
|
91
|
+
*/
|
|
92
|
+
setLocale(locale) {
|
|
93
|
+
this.locale = locale;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* 设置自定义多语言文案
|
|
97
|
+
*/
|
|
98
|
+
setLocales(locales) {
|
|
99
|
+
this.locales = { ...defaultLocales, ...locales };
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* 获取多语言文案
|
|
103
|
+
*/
|
|
104
|
+
getText(key) {
|
|
105
|
+
var _a, _b;
|
|
106
|
+
return ((_b = (_a = this.locales) == null ? void 0 : _a[this.locale]) == null ? void 0 : _b[key]) || key;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* 获取多语言名称
|
|
110
|
+
*/
|
|
111
|
+
getLocalizedName(name) {
|
|
112
|
+
if (typeof name === "string") {
|
|
113
|
+
return name;
|
|
114
|
+
}
|
|
115
|
+
return name[this.locale] || name["en"] || Object.values(name)[0] || "";
|
|
116
|
+
}
|
|
117
|
+
// ============================================
|
|
118
|
+
// 核心评估方法
|
|
119
|
+
// ============================================
|
|
120
|
+
/**
|
|
121
|
+
* 评估商品列表
|
|
122
|
+
*
|
|
123
|
+
* 判断每个商品适用哪些促销活动
|
|
124
|
+
* 支持主商品和 bundle 子商品的匹配
|
|
125
|
+
*
|
|
126
|
+
* @param input 评估输入
|
|
127
|
+
* @returns 每个商品的促销评估结果
|
|
128
|
+
*/
|
|
129
|
+
evaluateProducts(input) {
|
|
130
|
+
const { products, strategyConfigs, channel } = input;
|
|
131
|
+
const configs = strategyConfigs || this.strategyConfigs;
|
|
132
|
+
const results = [];
|
|
133
|
+
for (const product of products) {
|
|
134
|
+
const applicablePromotions = [];
|
|
135
|
+
for (const config of configs) {
|
|
136
|
+
const matchInfo = this.getProductMatchInfo(product, config);
|
|
137
|
+
if (!matchInfo.isMatch) {
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
let contextProduct = product;
|
|
141
|
+
if (matchInfo.matchedBundleIndex !== void 0 && product.bundle) {
|
|
142
|
+
const bundleItem = product.bundle[matchInfo.matchedBundleIndex];
|
|
143
|
+
contextProduct = {
|
|
144
|
+
...product,
|
|
145
|
+
product_id: bundleItem._bundle_product_id,
|
|
146
|
+
product_variant_id: bundleItem.product_variant_id
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
const context = this.adapter.prepareContext({
|
|
150
|
+
products: [contextProduct],
|
|
151
|
+
currentProduct: contextProduct,
|
|
152
|
+
channel
|
|
153
|
+
});
|
|
154
|
+
const result = this.engine.evaluate(config, context);
|
|
155
|
+
if (result.applicable && result.matchedActions.length > 0) {
|
|
156
|
+
const action = result.matchedActions[0];
|
|
157
|
+
const transformedResult = this.adapter.transformResult(result, {
|
|
158
|
+
products: [product],
|
|
159
|
+
currentProduct: product,
|
|
160
|
+
channel
|
|
161
|
+
});
|
|
162
|
+
if (transformedResult.actionDetail) {
|
|
163
|
+
applicablePromotions.push({
|
|
164
|
+
strategyId: config.metadata.id,
|
|
165
|
+
strategyName: config.metadata.name,
|
|
166
|
+
actionType: action.type,
|
|
167
|
+
actionDetail: transformedResult.actionDetail,
|
|
168
|
+
display: this.getDisplayConfig(config),
|
|
169
|
+
strategyConfig: config,
|
|
170
|
+
// 记录匹配的是哪个 bundle 子商品
|
|
171
|
+
matchedBundleIndex: matchInfo.matchedBundleIndex
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
results.push({
|
|
177
|
+
product,
|
|
178
|
+
applicablePromotions,
|
|
179
|
+
hasPromotion: applicablePromotions.length > 0
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
return results;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* 评估购物车
|
|
186
|
+
*
|
|
187
|
+
* 返回所有适用的促销及按促销分组的商品
|
|
188
|
+
* 支持 bundle 子商品的数量计算(主商品数量 × 子商品数量)
|
|
189
|
+
*
|
|
190
|
+
* @param input 评估输入
|
|
191
|
+
* @returns 购物车评估结果
|
|
192
|
+
*/
|
|
193
|
+
evaluateCart(input) {
|
|
194
|
+
const { products, strategyConfigs, channel } = input;
|
|
195
|
+
const configs = strategyConfigs || this.strategyConfigs;
|
|
196
|
+
const productResults = this.evaluateProducts(input);
|
|
197
|
+
const promotionMap = /* @__PURE__ */ new Map();
|
|
198
|
+
for (const productResult of productResults) {
|
|
199
|
+
for (const promo of productResult.applicablePromotions) {
|
|
200
|
+
const existing = promotionMap.get(promo.strategyId);
|
|
201
|
+
const product = productResult.product;
|
|
202
|
+
let promoQuantity = product.quantity;
|
|
203
|
+
if (promo.matchedBundleIndex !== void 0 && product.bundle) {
|
|
204
|
+
const bundleItem = product.bundle[promo.matchedBundleIndex];
|
|
205
|
+
promoQuantity = product.quantity * (bundleItem.quantity || 1);
|
|
206
|
+
}
|
|
207
|
+
if (existing) {
|
|
208
|
+
existing.applicableProducts.push(product);
|
|
209
|
+
existing.totalQuantity += promoQuantity;
|
|
210
|
+
existing.totalAmount += product.price * product.quantity;
|
|
211
|
+
if (!existing.productMatchedBundleIndexMap) {
|
|
212
|
+
existing.productMatchedBundleIndexMap = /* @__PURE__ */ new Map();
|
|
213
|
+
}
|
|
214
|
+
existing.productMatchedBundleIndexMap.set(product.id, promo.matchedBundleIndex);
|
|
215
|
+
} else {
|
|
216
|
+
const productMatchedBundleIndexMap = /* @__PURE__ */ new Map();
|
|
217
|
+
productMatchedBundleIndexMap.set(product.id, promo.matchedBundleIndex);
|
|
218
|
+
promotionMap.set(promo.strategyId, {
|
|
219
|
+
...promo,
|
|
220
|
+
applicableProducts: [product],
|
|
221
|
+
totalQuantity: promoQuantity,
|
|
222
|
+
totalAmount: product.price * product.quantity,
|
|
223
|
+
productMatchedBundleIndexMap
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
const applicablePromotions = Array.from(promotionMap.values());
|
|
229
|
+
const promotionGroups = applicablePromotions.map(
|
|
230
|
+
(promo) => {
|
|
231
|
+
var _a;
|
|
232
|
+
return {
|
|
233
|
+
strategyId: promo.strategyId,
|
|
234
|
+
strategyName: promo.strategyName,
|
|
235
|
+
strategyMetadata: (_a = promo.strategyConfig) == null ? void 0 : _a.metadata,
|
|
236
|
+
strategyConfig: promo.strategyConfig,
|
|
237
|
+
actionType: promo.actionType,
|
|
238
|
+
actionDetail: promo.actionDetail,
|
|
239
|
+
products: promo.applicableProducts,
|
|
240
|
+
totalQuantity: promo.totalQuantity,
|
|
241
|
+
totalAmount: promo.totalAmount,
|
|
242
|
+
// 传递 productMatchedBundleIndexMap 到 group
|
|
243
|
+
productMatchedBundleIndexMap: promo.productMatchedBundleIndexMap
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
);
|
|
247
|
+
return {
|
|
248
|
+
productResults,
|
|
249
|
+
applicablePromotions,
|
|
250
|
+
promotionGroups
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* 评估购物车并计算定价
|
|
255
|
+
*
|
|
256
|
+
* 返回处理后的商品数组(包含拆分、finalPrice)和赠品信息
|
|
257
|
+
* - 对于 X_ITEMS_FOR_Y_PRICE:按原价比例均摊价格,优先使用高价商品
|
|
258
|
+
* - 对于 BUY_X_GET_Y_FREE:计算赠品数量和可选赠品列表
|
|
259
|
+
*
|
|
260
|
+
* @param input 评估输入
|
|
261
|
+
* @returns 带定价的购物车评估结果
|
|
262
|
+
*/
|
|
263
|
+
evaluateCartWithPricing(input) {
|
|
264
|
+
const { products } = input;
|
|
265
|
+
const cartResult = this.evaluateCart(input);
|
|
266
|
+
const sortedGroups = this.sortPromotionGroupsByPriority(
|
|
267
|
+
cartResult.promotionGroups,
|
|
268
|
+
cartResult.applicablePromotions
|
|
269
|
+
);
|
|
270
|
+
const processedQuantityMap = /* @__PURE__ */ new Map();
|
|
271
|
+
const pricedProducts = [];
|
|
272
|
+
const gifts = [];
|
|
273
|
+
let totalOriginalAmount = new import_decimal.default(0);
|
|
274
|
+
let totalFinalAmount = new import_decimal.default(0);
|
|
275
|
+
for (const group of sortedGroups) {
|
|
276
|
+
const { actionType } = group;
|
|
277
|
+
const matchedBundleIndexMap = group.productMatchedBundleIndexMap || /* @__PURE__ */ new Map();
|
|
278
|
+
if (actionType === import_type.PROMOTION_ACTION_TYPES.X_ITEMS_FOR_Y_PRICE) {
|
|
279
|
+
const result = this.processXItemsForYPrice(
|
|
280
|
+
group,
|
|
281
|
+
processedQuantityMap,
|
|
282
|
+
matchedBundleIndexMap
|
|
283
|
+
);
|
|
284
|
+
pricedProducts.push(...result.products);
|
|
285
|
+
totalOriginalAmount = totalOriginalAmount.plus(result.originalAmount);
|
|
286
|
+
totalFinalAmount = totalFinalAmount.plus(result.finalAmount);
|
|
287
|
+
} else if (actionType === import_type.PROMOTION_ACTION_TYPES.BUY_X_GET_Y_FREE) {
|
|
288
|
+
const result = this.processBuyXGetYFree(
|
|
289
|
+
group,
|
|
290
|
+
processedQuantityMap,
|
|
291
|
+
matchedBundleIndexMap
|
|
292
|
+
);
|
|
293
|
+
pricedProducts.push(...result.products);
|
|
294
|
+
totalOriginalAmount = totalOriginalAmount.plus(result.originalAmount);
|
|
295
|
+
totalFinalAmount = totalFinalAmount.plus(result.finalAmount);
|
|
296
|
+
if (result.giftInfo) {
|
|
297
|
+
gifts.push(result.giftInfo);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
const remainingProducts = this.getRemainingProducts(
|
|
302
|
+
products,
|
|
303
|
+
processedQuantityMap
|
|
304
|
+
);
|
|
305
|
+
for (const product of remainingProducts) {
|
|
306
|
+
const amount = new import_decimal.default(product.price).mul(product.quantity);
|
|
307
|
+
totalOriginalAmount = totalOriginalAmount.plus(amount);
|
|
308
|
+
totalFinalAmount = totalFinalAmount.plus(amount);
|
|
309
|
+
}
|
|
310
|
+
pricedProducts.push(...remainingProducts);
|
|
311
|
+
const totalDiscount = totalOriginalAmount.minus(totalFinalAmount);
|
|
312
|
+
return {
|
|
313
|
+
products: pricedProducts,
|
|
314
|
+
gifts,
|
|
315
|
+
totalDiscount: import_decimal.default.max(0, totalDiscount).toNumber()
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* 获取商品适用的促销列表
|
|
320
|
+
*
|
|
321
|
+
* 简化方法,用于商品卡片展示
|
|
322
|
+
*
|
|
323
|
+
* @param product 商品
|
|
324
|
+
* @param strategyConfigs 策略配置(可选)
|
|
325
|
+
* @returns 适用的促销列表
|
|
326
|
+
*/
|
|
327
|
+
getProductPromotions(product, strategyConfigs) {
|
|
328
|
+
var _a;
|
|
329
|
+
const results = this.evaluateProducts({
|
|
330
|
+
products: [product],
|
|
331
|
+
strategyConfigs
|
|
332
|
+
});
|
|
333
|
+
return ((_a = results[0]) == null ? void 0 : _a.applicablePromotions) || [];
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* 获取商品的促销标签
|
|
337
|
+
*
|
|
338
|
+
* 用于商品卡片展示
|
|
339
|
+
*
|
|
340
|
+
* @param product 商品
|
|
341
|
+
* @param strategyConfigs 策略配置(可选)
|
|
342
|
+
* @returns 促销标签列表
|
|
343
|
+
*/
|
|
344
|
+
getProductPromotionTags(product, strategyConfigs) {
|
|
345
|
+
const promotions = this.getProductPromotions(product, strategyConfigs);
|
|
346
|
+
return promotions.filter((promo) => promo.display).map((promo) => ({
|
|
347
|
+
text: this.getLocalizedName(promo.display.text),
|
|
348
|
+
type: promo.display.type,
|
|
349
|
+
strategyId: promo.strategyId
|
|
350
|
+
}));
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* 查找商品适用的策略配置
|
|
354
|
+
*
|
|
355
|
+
* @param product 商品
|
|
356
|
+
* @param strategyConfigs 策略配置列表(可选)
|
|
357
|
+
* @returns 适用的策略配置列表
|
|
358
|
+
*/
|
|
359
|
+
findApplicableStrategies(product, strategyConfigs) {
|
|
360
|
+
const configs = strategyConfigs || this.strategyConfigs;
|
|
361
|
+
return configs.filter(
|
|
362
|
+
(config) => this.isProductInStrategy(product, config)
|
|
363
|
+
);
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* 批量获取商品列表的适用策略信息
|
|
367
|
+
*
|
|
368
|
+
* 用于给商品列表追加策略标签信息
|
|
369
|
+
* 只检查商品 ID 匹配和策略时间范围
|
|
370
|
+
*
|
|
371
|
+
* @param input 评估输入(商品列表)
|
|
372
|
+
* @param matchVariant 是否需要匹配 product_variant_id,默认 true(严格匹配),false 时只匹配 product_id
|
|
373
|
+
* @returns 每个商品的适用策略完整数据
|
|
374
|
+
*/
|
|
375
|
+
getProductsApplicableStrategies(input, matchVariant = true) {
|
|
376
|
+
const { products, strategyConfigs, channel } = input;
|
|
377
|
+
const configs = strategyConfigs || this.strategyConfigs;
|
|
378
|
+
const results = [];
|
|
379
|
+
for (const product of products) {
|
|
380
|
+
const applicableStrategies = [];
|
|
381
|
+
for (const config of configs) {
|
|
382
|
+
let contextProduct = product;
|
|
383
|
+
if (!matchVariant) {
|
|
384
|
+
const productMatchRule = this.findProductMatchRule(config);
|
|
385
|
+
if (productMatchRule) {
|
|
386
|
+
const configProducts = productMatchRule.value;
|
|
387
|
+
const matchedConfig = configProducts.find(
|
|
388
|
+
(item) => item.product_id === product.product_id
|
|
389
|
+
);
|
|
390
|
+
if (!matchedConfig) {
|
|
391
|
+
continue;
|
|
392
|
+
}
|
|
393
|
+
if (matchedConfig.product_variant_id !== 0 && matchedConfig.product_variant_id !== product.product_variant_id) {
|
|
394
|
+
contextProduct = {
|
|
395
|
+
...product,
|
|
396
|
+
product_variant_id: matchedConfig.product_variant_id
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
const context = this.adapter.prepareContext({
|
|
402
|
+
products: [contextProduct],
|
|
403
|
+
currentProduct: contextProduct,
|
|
404
|
+
channel
|
|
405
|
+
});
|
|
406
|
+
const result = this.engine.evaluate(config, context);
|
|
407
|
+
if (result.applicable && result.matchedActions.length > 0) {
|
|
408
|
+
const action = result.matchedActions[0];
|
|
409
|
+
const transformedResult = this.adapter.transformResult(result, {
|
|
410
|
+
products: [product],
|
|
411
|
+
currentProduct: product,
|
|
412
|
+
channel
|
|
413
|
+
});
|
|
414
|
+
if (transformedResult.actionDetail) {
|
|
415
|
+
const { requiredQuantity, eligibleProducts } = this.getPromotionRequirements(
|
|
416
|
+
action.type,
|
|
417
|
+
transformedResult.actionDetail,
|
|
418
|
+
config
|
|
419
|
+
);
|
|
420
|
+
applicableStrategies.push({
|
|
421
|
+
strategyId: config.metadata.id,
|
|
422
|
+
strategyName: config.metadata.name,
|
|
423
|
+
strategyMetadata: config.metadata,
|
|
424
|
+
actionType: action.type,
|
|
425
|
+
actionDetail: transformedResult.actionDetail,
|
|
426
|
+
display: this.getDisplayConfig(config),
|
|
427
|
+
strategyConfig: config,
|
|
428
|
+
requiredQuantity,
|
|
429
|
+
eligibleProducts
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
results.push({
|
|
435
|
+
product,
|
|
436
|
+
applicableStrategies,
|
|
437
|
+
hasApplicableStrategy: applicableStrategies.length > 0
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
return results;
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* 获取促销所需数量和可参与商品列表
|
|
444
|
+
*
|
|
445
|
+
* @param actionType 促销类型
|
|
446
|
+
* @param actionDetail 促销详情
|
|
447
|
+
* @param config 策略配置
|
|
448
|
+
* @returns { requiredQuantity, eligibleProducts }
|
|
449
|
+
*/
|
|
450
|
+
getPromotionRequirements(actionType, actionDetail, config) {
|
|
451
|
+
let requiredQuantity = 0;
|
|
452
|
+
if (actionType === "BUY_X_GET_Y_FREE") {
|
|
453
|
+
const detail = actionDetail;
|
|
454
|
+
requiredQuantity = detail.buyQuantity || 0;
|
|
455
|
+
} else if (actionType === "X_ITEMS_FOR_Y_PRICE") {
|
|
456
|
+
const detail = actionDetail;
|
|
457
|
+
requiredQuantity = detail.x || 0;
|
|
458
|
+
}
|
|
459
|
+
const productMatchRule = this.findProductMatchRule(config);
|
|
460
|
+
const eligibleProducts = (productMatchRule == null ? void 0 : productMatchRule.value) || [];
|
|
461
|
+
return {
|
|
462
|
+
requiredQuantity,
|
|
463
|
+
eligibleProducts
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
// ============================================
|
|
467
|
+
// 私有辅助方法
|
|
468
|
+
// ============================================
|
|
469
|
+
/**
|
|
470
|
+
* 按优先级排序促销组
|
|
471
|
+
* 优先级从 strategyConfig.actions[0].priority 获取,数值越小优先级越高
|
|
472
|
+
*/
|
|
473
|
+
sortPromotionGroupsByPriority(groups, promotions) {
|
|
474
|
+
var _a, _b;
|
|
475
|
+
const priorityMap = /* @__PURE__ */ new Map();
|
|
476
|
+
for (const promo of promotions) {
|
|
477
|
+
const actions = ((_a = promo.strategyConfig) == null ? void 0 : _a.actions) || [];
|
|
478
|
+
const priority = ((_b = actions[0]) == null ? void 0 : _b.priority) ?? 999;
|
|
479
|
+
priorityMap.set(promo.strategyId, priority);
|
|
480
|
+
}
|
|
481
|
+
return [...groups].sort((a, b) => {
|
|
482
|
+
const priorityA = priorityMap.get(a.strategyId) ?? 999;
|
|
483
|
+
const priorityB = priorityMap.get(b.strategyId) ?? 999;
|
|
484
|
+
return priorityA - priorityB;
|
|
485
|
+
});
|
|
486
|
+
}
|
|
487
|
+
/**
|
|
488
|
+
* 处理 X件Y元 促销
|
|
489
|
+
*
|
|
490
|
+
* 1. 展开商品为单件列表,按价格从高到低排序
|
|
491
|
+
* 2. 计算可凑成的组数
|
|
492
|
+
* 3. 按原价比例均摊价格
|
|
493
|
+
* 4. 拆分商品(部分参与促销、部分原价)
|
|
494
|
+
*
|
|
495
|
+
* 注意:每个商品按 id 独立处理,不会合并不同 id 的商品
|
|
496
|
+
*/
|
|
497
|
+
processXItemsForYPrice(group, processedQuantityMap, matchedBundleIndexMap) {
|
|
498
|
+
const { strategyId, strategyMetadata, actionDetail, products: groupProducts } = group;
|
|
499
|
+
const detail = actionDetail;
|
|
500
|
+
const { x, price: groupPrice, cumulative } = detail;
|
|
501
|
+
const result = [];
|
|
502
|
+
let originalAmount = new import_decimal.default(0);
|
|
503
|
+
let finalAmount = new import_decimal.default(0);
|
|
504
|
+
const unitProducts = [];
|
|
505
|
+
for (const product of groupProducts) {
|
|
506
|
+
const key = this.getProductKey(product);
|
|
507
|
+
const processedQty = processedQuantityMap.get(key) || 0;
|
|
508
|
+
const availableQty = product.quantity - processedQty;
|
|
509
|
+
if (availableQty <= 0)
|
|
510
|
+
continue;
|
|
511
|
+
for (let i = 0; i < availableQty; i++) {
|
|
512
|
+
unitProducts.push({
|
|
513
|
+
product,
|
|
514
|
+
price: product.price,
|
|
515
|
+
productId: product.id
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
if (unitProducts.length === 0) {
|
|
520
|
+
return { products: [], originalAmount: 0, finalAmount: 0 };
|
|
521
|
+
}
|
|
522
|
+
unitProducts.sort((a, b) => b.price - a.price);
|
|
523
|
+
const totalUnits = unitProducts.length;
|
|
524
|
+
const groupCount = cumulative ? Math.floor(totalUnits / x) : totalUnits >= x ? 1 : 0;
|
|
525
|
+
const promotionUnits = groupCount * x;
|
|
526
|
+
const inPromotionUnits = unitProducts.slice(0, promotionUnits);
|
|
527
|
+
const notInPromotionUnits = unitProducts.slice(promotionUnits);
|
|
528
|
+
const promotionQtyByProductId = /* @__PURE__ */ new Map();
|
|
529
|
+
for (const unit of inPromotionUnits) {
|
|
530
|
+
const id = unit.productId;
|
|
531
|
+
promotionQtyByProductId.set(id, (promotionQtyByProductId.get(id) || 0) + 1);
|
|
532
|
+
}
|
|
533
|
+
const remainingQtyByProductId = /* @__PURE__ */ new Map();
|
|
534
|
+
for (const unit of notInPromotionUnits) {
|
|
535
|
+
const id = unit.productId;
|
|
536
|
+
remainingQtyByProductId.set(id, (remainingQtyByProductId.get(id) || 0) + 1);
|
|
537
|
+
}
|
|
538
|
+
const allocatedPricesByProductId = /* @__PURE__ */ new Map();
|
|
539
|
+
for (let g = 0; g < groupCount; g++) {
|
|
540
|
+
const startIdx = g * x;
|
|
541
|
+
const endIdx = startIdx + x;
|
|
542
|
+
const groupUnits = inPromotionUnits.slice(startIdx, endIdx);
|
|
543
|
+
const groupOriginalTotal = groupUnits.reduce(
|
|
544
|
+
(sum, u) => sum.plus(u.price),
|
|
545
|
+
new import_decimal.default(0)
|
|
546
|
+
);
|
|
547
|
+
const groupPriceDecimal = new import_decimal.default(groupPrice);
|
|
548
|
+
let allocatedSum = new import_decimal.default(0);
|
|
549
|
+
for (let i = 0; i < groupUnits.length; i++) {
|
|
550
|
+
const unit = groupUnits[i];
|
|
551
|
+
const isLastUnit = i === groupUnits.length - 1;
|
|
552
|
+
const ratio = groupOriginalTotal.eq(0) ? new import_decimal.default(0) : new import_decimal.default(unit.price).div(groupOriginalTotal);
|
|
553
|
+
const allocatedDecimal = isLastUnit ? groupPriceDecimal.minus(allocatedSum) : groupPriceDecimal.mul(ratio);
|
|
554
|
+
const productId = unit.productId;
|
|
555
|
+
const prices = allocatedPricesByProductId.get(productId) || [];
|
|
556
|
+
prices.push(allocatedDecimal);
|
|
557
|
+
allocatedPricesByProductId.set(productId, prices);
|
|
558
|
+
allocatedSum = allocatedSum.plus(allocatedDecimal);
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
for (const product of groupProducts) {
|
|
562
|
+
const key = this.getProductKey(product);
|
|
563
|
+
const processedQty = processedQuantityMap.get(key) || 0;
|
|
564
|
+
const availableQty = product.quantity - processedQty;
|
|
565
|
+
if (availableQty <= 0)
|
|
566
|
+
continue;
|
|
567
|
+
const promoQty = promotionQtyByProductId.get(product.id) || 0;
|
|
568
|
+
const remainingQty = remainingQtyByProductId.get(product.id) || 0;
|
|
569
|
+
const allocatedPrices = allocatedPricesByProductId.get(product.id) || [];
|
|
570
|
+
const matchedBundleIndex = matchedBundleIndexMap == null ? void 0 : matchedBundleIndexMap.get(product.id);
|
|
571
|
+
if (promoQty > 0 && remainingQty === 0) {
|
|
572
|
+
const totalAllocated = allocatedPrices.reduce(
|
|
573
|
+
(sum, p) => sum.plus(p),
|
|
574
|
+
new import_decimal.default(0)
|
|
575
|
+
);
|
|
576
|
+
const finalPricePerUnit = this.formatPrice(totalAllocated.div(promoQty));
|
|
577
|
+
result.push({
|
|
578
|
+
...product,
|
|
579
|
+
quantity: promoQty,
|
|
580
|
+
finalPrice: finalPricePerUnit,
|
|
581
|
+
strategyId,
|
|
582
|
+
strategyMetadata,
|
|
583
|
+
inPromotion: true,
|
|
584
|
+
isSplit: false,
|
|
585
|
+
matchedBundleIndex
|
|
586
|
+
});
|
|
587
|
+
const productOriginalAmount = new import_decimal.default(product.price).mul(promoQty);
|
|
588
|
+
originalAmount = originalAmount.plus(productOriginalAmount);
|
|
589
|
+
finalAmount = finalAmount.plus(totalAllocated);
|
|
590
|
+
} else if (promoQty === 0 && remainingQty > 0) {
|
|
591
|
+
result.push({
|
|
592
|
+
...product,
|
|
593
|
+
quantity: remainingQty,
|
|
594
|
+
finalPrice: this.formatPrice(product.price),
|
|
595
|
+
strategyId: void 0,
|
|
596
|
+
strategyMetadata: void 0,
|
|
597
|
+
inPromotion: false,
|
|
598
|
+
isSplit: false
|
|
599
|
+
});
|
|
600
|
+
const amount = new import_decimal.default(product.price).mul(remainingQty);
|
|
601
|
+
originalAmount = originalAmount.plus(amount);
|
|
602
|
+
finalAmount = finalAmount.plus(amount);
|
|
603
|
+
} else if (promoQty > 0 && remainingQty > 0) {
|
|
604
|
+
const totalAllocated = allocatedPrices.reduce(
|
|
605
|
+
(sum, p) => sum.plus(p),
|
|
606
|
+
new import_decimal.default(0)
|
|
607
|
+
);
|
|
608
|
+
const finalPricePerUnit = this.formatPrice(totalAllocated.div(promoQty));
|
|
609
|
+
result.push({
|
|
610
|
+
...product,
|
|
611
|
+
id: this.generateRandomId(),
|
|
612
|
+
originalId: product.id,
|
|
613
|
+
quantity: promoQty,
|
|
614
|
+
finalPrice: finalPricePerUnit,
|
|
615
|
+
strategyId,
|
|
616
|
+
strategyMetadata,
|
|
617
|
+
inPromotion: true,
|
|
618
|
+
matchedBundleIndex,
|
|
619
|
+
isSplit: true
|
|
620
|
+
});
|
|
621
|
+
const promoOriginalAmount = new import_decimal.default(product.price).mul(promoQty);
|
|
622
|
+
originalAmount = originalAmount.plus(promoOriginalAmount);
|
|
623
|
+
finalAmount = finalAmount.plus(totalAllocated);
|
|
624
|
+
result.push({
|
|
625
|
+
...product,
|
|
626
|
+
quantity: remainingQty,
|
|
627
|
+
finalPrice: this.formatPrice(product.price),
|
|
628
|
+
strategyId: void 0,
|
|
629
|
+
strategyMetadata: void 0,
|
|
630
|
+
inPromotion: false,
|
|
631
|
+
isSplit: true
|
|
632
|
+
});
|
|
633
|
+
const remainingAmount = new import_decimal.default(product.price).mul(remainingQty);
|
|
634
|
+
originalAmount = originalAmount.plus(remainingAmount);
|
|
635
|
+
finalAmount = finalAmount.plus(remainingAmount);
|
|
636
|
+
}
|
|
637
|
+
processedQuantityMap.set(key, processedQty + availableQty);
|
|
638
|
+
}
|
|
639
|
+
return {
|
|
640
|
+
products: result,
|
|
641
|
+
originalAmount: originalAmount.toNumber(),
|
|
642
|
+
finalAmount: finalAmount.toNumber()
|
|
643
|
+
};
|
|
644
|
+
}
|
|
645
|
+
/**
|
|
646
|
+
* 处理 买X送Y 促销
|
|
647
|
+
*
|
|
648
|
+
* 计算赠品数量,商品本身价格不变
|
|
649
|
+
* 支持 bundle 子商品的数量计算(主商品数量 × 子商品数量)
|
|
650
|
+
*/
|
|
651
|
+
processBuyXGetYFree(group, processedQuantityMap, matchedBundleIndexMap) {
|
|
652
|
+
const {
|
|
653
|
+
strategyId,
|
|
654
|
+
strategyName,
|
|
655
|
+
strategyMetadata,
|
|
656
|
+
actionDetail,
|
|
657
|
+
products: groupProducts
|
|
658
|
+
} = group;
|
|
659
|
+
const detail = actionDetail;
|
|
660
|
+
const { buyQuantity, freeQuantity, cumulative, giftProducts } = detail;
|
|
661
|
+
const result = [];
|
|
662
|
+
let originalAmount = new import_decimal.default(0);
|
|
663
|
+
let finalAmount = new import_decimal.default(0);
|
|
664
|
+
let totalPromoQty = 0;
|
|
665
|
+
const productInfoMap = /* @__PURE__ */ new Map();
|
|
666
|
+
for (const product of groupProducts) {
|
|
667
|
+
const key = this.getProductKey(product);
|
|
668
|
+
const processedQty = processedQuantityMap.get(key) || 0;
|
|
669
|
+
const availableQty = product.quantity - processedQty;
|
|
670
|
+
if (availableQty <= 0)
|
|
671
|
+
continue;
|
|
672
|
+
const matchedBundleIndex = matchedBundleIndexMap == null ? void 0 : matchedBundleIndexMap.get(product.id);
|
|
673
|
+
let promoQty = availableQty;
|
|
674
|
+
if (matchedBundleIndex !== void 0 && product.bundle) {
|
|
675
|
+
const bundleItem = product.bundle[matchedBundleIndex];
|
|
676
|
+
promoQty = availableQty * (bundleItem.quantity || 1);
|
|
677
|
+
}
|
|
678
|
+
totalPromoQty += promoQty;
|
|
679
|
+
productInfoMap.set(key, { availableQty, promoQty, matchedBundleIndex });
|
|
680
|
+
}
|
|
681
|
+
const isFulfilled = totalPromoQty >= buyQuantity;
|
|
682
|
+
for (const product of groupProducts) {
|
|
683
|
+
const key = this.getProductKey(product);
|
|
684
|
+
const info = productInfoMap.get(key);
|
|
685
|
+
if (!info || info.availableQty <= 0)
|
|
686
|
+
continue;
|
|
687
|
+
const processedQty = processedQuantityMap.get(key) || 0;
|
|
688
|
+
result.push({
|
|
689
|
+
...product,
|
|
690
|
+
quantity: info.availableQty,
|
|
691
|
+
// 返回主商品数量
|
|
692
|
+
finalPrice: this.formatPrice(product.price),
|
|
693
|
+
strategyId,
|
|
694
|
+
strategyMetadata,
|
|
695
|
+
inPromotion: isFulfilled,
|
|
696
|
+
isSplit: info.availableQty < product.quantity,
|
|
697
|
+
matchedBundleIndex: info.matchedBundleIndex
|
|
698
|
+
});
|
|
699
|
+
const amount = new import_decimal.default(product.price).mul(info.availableQty);
|
|
700
|
+
originalAmount = originalAmount.plus(amount);
|
|
701
|
+
finalAmount = finalAmount.plus(amount);
|
|
702
|
+
processedQuantityMap.set(key, processedQty + info.availableQty);
|
|
703
|
+
}
|
|
704
|
+
let giftCount = 0;
|
|
705
|
+
if (isFulfilled) {
|
|
706
|
+
giftCount = cumulative ? Math.floor(totalPromoQty / buyQuantity) * freeQuantity : freeQuantity;
|
|
707
|
+
}
|
|
708
|
+
const giftInfo = giftCount > 0 ? {
|
|
709
|
+
strategyId,
|
|
710
|
+
strategyName,
|
|
711
|
+
giftCount,
|
|
712
|
+
giftOptions: giftProducts || []
|
|
713
|
+
} : null;
|
|
714
|
+
return {
|
|
715
|
+
products: result,
|
|
716
|
+
originalAmount: originalAmount.toNumber(),
|
|
717
|
+
finalAmount: finalAmount.toNumber(),
|
|
718
|
+
giftInfo
|
|
719
|
+
};
|
|
720
|
+
}
|
|
721
|
+
/**
|
|
722
|
+
* 获取未参与任何促销的商品
|
|
723
|
+
*/
|
|
724
|
+
getRemainingProducts(allProducts, processedQuantityMap) {
|
|
725
|
+
const result = [];
|
|
726
|
+
for (const product of allProducts) {
|
|
727
|
+
const key = this.getProductKey(product);
|
|
728
|
+
const processedQty = processedQuantityMap.get(key) || 0;
|
|
729
|
+
const remainingQty = product.quantity - processedQty;
|
|
730
|
+
if (remainingQty > 0) {
|
|
731
|
+
result.push({
|
|
732
|
+
...product,
|
|
733
|
+
quantity: remainingQty,
|
|
734
|
+
finalPrice: this.formatPrice(product.price),
|
|
735
|
+
strategyId: void 0,
|
|
736
|
+
inPromotion: false,
|
|
737
|
+
isSplit: remainingQty < product.quantity
|
|
738
|
+
});
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
return result;
|
|
742
|
+
}
|
|
743
|
+
/**
|
|
744
|
+
* 价格统一保留两位小数
|
|
745
|
+
*/
|
|
746
|
+
formatPrice(value) {
|
|
747
|
+
return new import_decimal.default(value).toDecimalPlaces(2, import_decimal.default.ROUND_HALF_UP).toNumber();
|
|
748
|
+
}
|
|
749
|
+
/**
|
|
750
|
+
* 获取商品唯一标识 key
|
|
751
|
+
* 使用商品的 id 作为唯一标识,不同 id 的商品不会被合并
|
|
752
|
+
*/
|
|
753
|
+
getProductKey(product) {
|
|
754
|
+
return String(product.id);
|
|
755
|
+
}
|
|
756
|
+
/**
|
|
757
|
+
* 生成随机ID
|
|
758
|
+
*/
|
|
759
|
+
generateRandomId() {
|
|
760
|
+
return Math.floor(Math.random() * 1e9) + Date.now();
|
|
761
|
+
}
|
|
762
|
+
/**
|
|
763
|
+
* 商品匹配结果信息
|
|
764
|
+
*/
|
|
765
|
+
getProductMatchInfo(product, config) {
|
|
766
|
+
const productMatchRule = this.findProductMatchRule(config);
|
|
767
|
+
if (!productMatchRule) {
|
|
768
|
+
return { isMatch: true };
|
|
769
|
+
}
|
|
770
|
+
const configProducts = productMatchRule.value;
|
|
771
|
+
if (this.isProductIdMatch(product.product_id, product.product_variant_id, configProducts)) {
|
|
772
|
+
return { isMatch: true };
|
|
773
|
+
}
|
|
774
|
+
if (product.bundle && product.bundle.length > 0) {
|
|
775
|
+
for (let i = 0; i < product.bundle.length; i++) {
|
|
776
|
+
const bundleItem = product.bundle[i];
|
|
777
|
+
if (this.isProductIdMatch(
|
|
778
|
+
bundleItem._bundle_product_id,
|
|
779
|
+
bundleItem.product_variant_id,
|
|
780
|
+
configProducts
|
|
781
|
+
)) {
|
|
782
|
+
return { isMatch: true, matchedBundleIndex: i };
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
return { isMatch: false };
|
|
787
|
+
}
|
|
788
|
+
/**
|
|
789
|
+
* 检查商品是否在策略的适用范围内
|
|
790
|
+
*/
|
|
791
|
+
isProductInStrategy(product, config) {
|
|
792
|
+
return this.getProductMatchInfo(product, config).isMatch;
|
|
793
|
+
}
|
|
794
|
+
/**
|
|
795
|
+
* 查找商品匹配规则
|
|
796
|
+
*/
|
|
797
|
+
findProductMatchRule(config) {
|
|
798
|
+
var _a;
|
|
799
|
+
if (!((_a = config == null ? void 0 : config.conditions) == null ? void 0 : _a.rules)) {
|
|
800
|
+
return null;
|
|
801
|
+
}
|
|
802
|
+
return config.conditions.rules.find(
|
|
803
|
+
(rule) => rule.field === "productIdAndVariantId" && (rule.operator === "product_match" || rule.operator === "object_in")
|
|
804
|
+
);
|
|
805
|
+
}
|
|
806
|
+
/**
|
|
807
|
+
* 检查商品ID是否匹配配置列表
|
|
808
|
+
*/
|
|
809
|
+
isProductIdMatch(productId, productVariantId, configProducts) {
|
|
810
|
+
return configProducts.some((config) => {
|
|
811
|
+
if (config.product_id !== productId) {
|
|
812
|
+
return false;
|
|
813
|
+
}
|
|
814
|
+
if (config.product_variant_id === 0) {
|
|
815
|
+
return true;
|
|
816
|
+
}
|
|
817
|
+
return config.product_variant_id === productVariantId;
|
|
818
|
+
});
|
|
819
|
+
}
|
|
820
|
+
/**
|
|
821
|
+
* 检查商品是否匹配配置(兼容旧方法)
|
|
822
|
+
*/
|
|
823
|
+
isProductMatch(product, configProducts) {
|
|
824
|
+
return this.isProductIdMatch(product.product_id, product.product_variant_id, configProducts);
|
|
825
|
+
}
|
|
826
|
+
/**
|
|
827
|
+
* 获取展示配置
|
|
828
|
+
*/
|
|
829
|
+
getDisplayConfig(config) {
|
|
830
|
+
var _a;
|
|
831
|
+
const custom = config.metadata.custom;
|
|
832
|
+
if ((_a = custom == null ? void 0 : custom.display) == null ? void 0 : _a.product_card) {
|
|
833
|
+
return {
|
|
834
|
+
text: custom.display.product_card.text,
|
|
835
|
+
type: custom.display.product_card.type
|
|
836
|
+
};
|
|
837
|
+
}
|
|
838
|
+
return void 0;
|
|
839
|
+
}
|
|
840
|
+
};
|
|
841
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
842
|
+
0 && (module.exports = {
|
|
843
|
+
PromotionEvaluator
|
|
844
|
+
});
|