@pisell/pisellos 1.0.142 → 1.0.143
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 +4 -0
- package/dist/model/strategy/adapter/index.js +4 -1
- package/dist/model/strategy/adapter/itemRule/adapter.d.ts +67 -0
- package/dist/model/strategy/adapter/itemRule/adapter.js +483 -0
- package/dist/model/strategy/adapter/itemRule/evaluator.d.ts +43 -0
- package/dist/model/strategy/adapter/itemRule/evaluator.js +169 -0
- package/dist/model/strategy/adapter/itemRule/examples.d.ts +79 -0
- package/dist/model/strategy/adapter/itemRule/examples.js +382 -0
- package/dist/model/strategy/adapter/itemRule/index.d.ts +5 -0
- package/dist/model/strategy/adapter/itemRule/index.js +5 -0
- package/dist/model/strategy/adapter/itemRule/type.d.ts +225 -0
- package/dist/model/strategy/adapter/itemRule/type.js +101 -0
- 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 +1279 -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.d.ts +2 -0
- package/dist/model/strategy/adapter/walletPass/evaluator.js +67 -10
- package/dist/model/strategy/adapter/walletPass/locales.js +12 -3
- package/dist/model/strategy/adapter/walletPass/type.d.ts +20 -2
- package/dist/model/strategy/adapter/walletPass/utils.d.ts +6 -6
- package/dist/model/strategy/adapter/walletPass/utils.js +143 -103
- package/dist/model/strategy/engine.d.ts +119 -0
- package/dist/model/strategy/engine.js +711 -0
- package/dist/model/strategy/index.d.ts +2 -93
- package/dist/model/strategy/index.js +6 -549
- package/dist/model/strategy/type.d.ts +17 -0
- package/dist/model/strategy/type.js +4 -0
- package/dist/solution/BookingByStep/index.d.ts +1 -1
- package/dist/solution/BookingTicket/index.d.ts +1 -1
- package/lib/model/strategy/adapter/index.d.ts +4 -0
- package/lib/model/strategy/adapter/index.js +12 -0
- package/lib/model/strategy/adapter/itemRule/adapter.d.ts +67 -0
- package/lib/model/strategy/adapter/itemRule/adapter.js +377 -0
- package/lib/model/strategy/adapter/itemRule/evaluator.d.ts +43 -0
- package/lib/model/strategy/adapter/itemRule/evaluator.js +124 -0
- package/lib/model/strategy/adapter/itemRule/examples.d.ts +79 -0
- package/lib/model/strategy/adapter/itemRule/examples.js +316 -0
- package/lib/model/strategy/adapter/itemRule/index.d.ts +5 -0
- package/lib/model/strategy/adapter/itemRule/index.js +60 -0
- package/lib/model/strategy/adapter/itemRule/type.d.ts +225 -0
- package/lib/model/strategy/adapter/itemRule/type.js +46 -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 +893 -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.d.ts +2 -0
- package/lib/model/strategy/adapter/walletPass/evaluator.js +38 -5
- package/lib/model/strategy/adapter/walletPass/locales.js +12 -3
- package/lib/model/strategy/adapter/walletPass/type.d.ts +20 -2
- package/lib/model/strategy/adapter/walletPass/utils.d.ts +6 -6
- package/lib/model/strategy/adapter/walletPass/utils.js +153 -82
- package/lib/model/strategy/engine.d.ts +119 -0
- package/lib/model/strategy/engine.js +514 -0
- package/lib/model/strategy/index.d.ts +2 -93
- package/lib/model/strategy/index.js +6 -381
- package/lib/model/strategy/type.d.ts +17 -0
- package/lib/solution/BookingByStep/index.d.ts +1 -1
- package/lib/solution/BookingTicket/index.d.ts +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,447 @@
|
|
|
1
|
+
import { StrategyConfig } from '../../type';
|
|
2
|
+
/**
|
|
3
|
+
* Promotion 策略类型定义
|
|
4
|
+
*
|
|
5
|
+
* 定义促销活动相关的 Action 类型、值结构和配置
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* 促销活动 Action 类型
|
|
9
|
+
*/
|
|
10
|
+
export declare const PROMOTION_ACTION_TYPES: {
|
|
11
|
+
/** X件Y元(每X件固定价格Y元,可累计) */
|
|
12
|
+
readonly X_ITEMS_FOR_Y_PRICE: "X_ITEMS_FOR_Y_PRICE";
|
|
13
|
+
/** 买X送Y(买X件送Y件,可累计) */
|
|
14
|
+
readonly BUY_X_GET_Y_FREE: "BUY_X_GET_Y_FREE";
|
|
15
|
+
/** 固定折扣 */
|
|
16
|
+
readonly DISCOUNT_RATE: "DISCOUNT_RATE";
|
|
17
|
+
/** 固定减价 */
|
|
18
|
+
readonly DEDUCT_AMOUNT: "DEDUCT_AMOUNT";
|
|
19
|
+
};
|
|
20
|
+
export type PromotionActionType = (typeof PROMOTION_ACTION_TYPES)[keyof typeof PROMOTION_ACTION_TYPES];
|
|
21
|
+
/**
|
|
22
|
+
* X件Y元 - Action Value
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* value: { x: 2, price: 10 } // 每2件10元
|
|
26
|
+
*/
|
|
27
|
+
export interface XItemsForYPriceValue {
|
|
28
|
+
/** 每X件(需要凑满的数量) */
|
|
29
|
+
x: number;
|
|
30
|
+
/** 固定价格Y元 */
|
|
31
|
+
price: number;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* X件Y元 - Action Config
|
|
35
|
+
*/
|
|
36
|
+
export interface XItemsForYPriceConfig {
|
|
37
|
+
/** 允许跨商品组合(A+B可以凑成一组) */
|
|
38
|
+
allowCrossProduct?: boolean;
|
|
39
|
+
/** 可累计(买5件 = 2组优惠 + 1件原价) */
|
|
40
|
+
cumulative?: boolean;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* 买X送Y - Action Value
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* value: { buyQuantity: 1, freeQuantity: 1 } // 买1送1
|
|
47
|
+
*/
|
|
48
|
+
export interface BuyXGetYFreeValue {
|
|
49
|
+
/** 买X件 */
|
|
50
|
+
buyQuantity: number;
|
|
51
|
+
/** 送Y件 */
|
|
52
|
+
freeQuantity: number;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* 赠品选择模式
|
|
56
|
+
*/
|
|
57
|
+
export type GiftSelectionMode = 'user_select' | 'auto' | 'cheapest';
|
|
58
|
+
/**
|
|
59
|
+
* 买X送Y - Action Config
|
|
60
|
+
*/
|
|
61
|
+
export interface BuyXGetYFreeConfig {
|
|
62
|
+
/** 赠品选择模式 */
|
|
63
|
+
giftSelectionMode?: GiftSelectionMode;
|
|
64
|
+
/** 可累计(买2送2、买3送3...) */
|
|
65
|
+
cumulative?: boolean;
|
|
66
|
+
/** 可选的赠品列表 */
|
|
67
|
+
giftProducts?: ProductIdentifier[];
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* 商品标识
|
|
71
|
+
*
|
|
72
|
+
* 匹配规则:
|
|
73
|
+
* - product_variant_id = 0 表示匹配任意变体
|
|
74
|
+
* - product_variant_id != 0 表示精确匹配该变体
|
|
75
|
+
*/
|
|
76
|
+
export interface ProductIdentifier {
|
|
77
|
+
/** 商品ID */
|
|
78
|
+
product_id: number;
|
|
79
|
+
/**
|
|
80
|
+
* 变体ID
|
|
81
|
+
* - 0 表示匹配任意变体
|
|
82
|
+
* - 非0 表示精确匹配该变体
|
|
83
|
+
*/
|
|
84
|
+
product_variant_id: number;
|
|
85
|
+
}
|
|
86
|
+
import { ActionEffect } from '../../type';
|
|
87
|
+
/**
|
|
88
|
+
* X件Y元 Action
|
|
89
|
+
*/
|
|
90
|
+
export interface XItemsForYPriceAction extends Omit<ActionEffect, 'type' | 'value' | 'config'> {
|
|
91
|
+
type: typeof PROMOTION_ACTION_TYPES.X_ITEMS_FOR_Y_PRICE;
|
|
92
|
+
value: XItemsForYPriceValue;
|
|
93
|
+
valueType: 'object';
|
|
94
|
+
config?: XItemsForYPriceConfig;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* 买X送Y Action
|
|
98
|
+
*/
|
|
99
|
+
export interface BuyXGetYFreeAction extends Omit<ActionEffect, 'type' | 'value' | 'config'> {
|
|
100
|
+
type: typeof PROMOTION_ACTION_TYPES.BUY_X_GET_Y_FREE;
|
|
101
|
+
value: BuyXGetYFreeValue;
|
|
102
|
+
valueType: 'object';
|
|
103
|
+
config?: BuyXGetYFreeConfig;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* 促销活动 Action 联合类型
|
|
107
|
+
*/
|
|
108
|
+
export type PromotionAction = XItemsForYPriceAction | BuyXGetYFreeAction;
|
|
109
|
+
/**
|
|
110
|
+
* 促销相关运算符
|
|
111
|
+
*/
|
|
112
|
+
export declare const PROMOTION_OPERATORS: {
|
|
113
|
+
/**
|
|
114
|
+
* 商品匹配运算符
|
|
115
|
+
*
|
|
116
|
+
* 匹配规则:
|
|
117
|
+
* - 首先匹配 product_id
|
|
118
|
+
* - 如果配置的 product_variant_id = 0,只需要 product_id 匹配
|
|
119
|
+
* - 如果配置的 product_variant_id != 0,还需要 product_variant_id 匹配
|
|
120
|
+
*/
|
|
121
|
+
readonly PRODUCT_MATCH: "product_match";
|
|
122
|
+
};
|
|
123
|
+
/**
|
|
124
|
+
* 促销活动业务数据
|
|
125
|
+
*
|
|
126
|
+
* 由业务层传入,用于策略评估
|
|
127
|
+
*/
|
|
128
|
+
export interface PromotionBusinessData {
|
|
129
|
+
/** 商品列表 */
|
|
130
|
+
products: PromotionProduct[];
|
|
131
|
+
/** 当前评估的商品(用于单商品场景,如商品卡片展示) */
|
|
132
|
+
currentProduct?: PromotionProduct;
|
|
133
|
+
/** 渠道 */
|
|
134
|
+
channel?: string;
|
|
135
|
+
/** 自定义属性 */
|
|
136
|
+
custom?: Record<string, any>;
|
|
137
|
+
}
|
|
138
|
+
type ProductBundleItem = {
|
|
139
|
+
/** 主商品ID */
|
|
140
|
+
product_id: number;
|
|
141
|
+
/** 变体ID(0 表示无变体) */
|
|
142
|
+
product_variant_id: number;
|
|
143
|
+
/** 数量 */
|
|
144
|
+
quantity: number;
|
|
145
|
+
/** 单价 */
|
|
146
|
+
price: number;
|
|
147
|
+
/** 原价 */
|
|
148
|
+
original_price?: number;
|
|
149
|
+
/** 套餐明细id */
|
|
150
|
+
id: number;
|
|
151
|
+
/** 套餐商品id */
|
|
152
|
+
_bundle_product_id: number;
|
|
153
|
+
};
|
|
154
|
+
/**
|
|
155
|
+
* 促销商品信息
|
|
156
|
+
*/
|
|
157
|
+
export interface PromotionProduct {
|
|
158
|
+
id: number;
|
|
159
|
+
/** 商品ID */
|
|
160
|
+
product_id: number;
|
|
161
|
+
/** 变体ID(0 表示无变体) */
|
|
162
|
+
product_variant_id: number;
|
|
163
|
+
/** 商品名称 */
|
|
164
|
+
name?: string;
|
|
165
|
+
/** 单价 */
|
|
166
|
+
price: number;
|
|
167
|
+
/** 商品套餐 */
|
|
168
|
+
bundle: ProductBundleItem[];
|
|
169
|
+
/** 数量 */
|
|
170
|
+
quantity: number;
|
|
171
|
+
/** 原价 */
|
|
172
|
+
original_price?: number;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* 促销活动适配器转换结果
|
|
176
|
+
*
|
|
177
|
+
* 返回给业务层,包含策略匹配信息
|
|
178
|
+
*/
|
|
179
|
+
export interface PromotionTransformResult {
|
|
180
|
+
/** 策略是否适用 */
|
|
181
|
+
isApplicable: boolean;
|
|
182
|
+
/** 匹配的 Action 类型 */
|
|
183
|
+
actionType?: PromotionActionType;
|
|
184
|
+
/** 匹配的 Action 详情 */
|
|
185
|
+
actionDetail?: PromotionActionDetail;
|
|
186
|
+
/** 适用的商品列表(从购物车商品中筛选出的适用商品) */
|
|
187
|
+
applicableProducts: PromotionProduct[];
|
|
188
|
+
/** 不适用的原因 */
|
|
189
|
+
reason?: string;
|
|
190
|
+
/** 不适用的原因码 */
|
|
191
|
+
reasonCode?: string;
|
|
192
|
+
/** 原始策略引擎结果 */
|
|
193
|
+
strategyResult: import('../../type').EvaluationResult;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Action 详情联合类型
|
|
197
|
+
*/
|
|
198
|
+
export type PromotionActionDetail = XItemsForYPriceActionDetail | BuyXGetYFreeActionDetail;
|
|
199
|
+
/**
|
|
200
|
+
* X件Y元 Action 详情
|
|
201
|
+
*/
|
|
202
|
+
export interface XItemsForYPriceActionDetail {
|
|
203
|
+
type: typeof PROMOTION_ACTION_TYPES.X_ITEMS_FOR_Y_PRICE;
|
|
204
|
+
/** 每X件 */
|
|
205
|
+
x: number;
|
|
206
|
+
/** 固定价格Y元 */
|
|
207
|
+
price: number;
|
|
208
|
+
/** 允许跨商品组合 */
|
|
209
|
+
allowCrossProduct: boolean;
|
|
210
|
+
/** 可累计 */
|
|
211
|
+
cumulative: boolean;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* 买X送Y Action 详情
|
|
215
|
+
*/
|
|
216
|
+
export interface BuyXGetYFreeActionDetail {
|
|
217
|
+
type: typeof PROMOTION_ACTION_TYPES.BUY_X_GET_Y_FREE;
|
|
218
|
+
/** 买X件 */
|
|
219
|
+
buyQuantity: number;
|
|
220
|
+
/** 送Y件 */
|
|
221
|
+
freeQuantity: number;
|
|
222
|
+
/** 赠品选择模式 */
|
|
223
|
+
giftSelectionMode: GiftSelectionMode;
|
|
224
|
+
/** 可累计 */
|
|
225
|
+
cumulative: boolean;
|
|
226
|
+
/** 可选的赠品列表 */
|
|
227
|
+
giftProducts: ProductIdentifier[];
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* X件Y元 - 业务层计算结果
|
|
231
|
+
*
|
|
232
|
+
* 由业务层根据 matchedAction 计算后返回
|
|
233
|
+
*/
|
|
234
|
+
export interface XItemsForYPriceResult {
|
|
235
|
+
/** 满足条件的组数 */
|
|
236
|
+
groupCount: number;
|
|
237
|
+
/** 剩余件数(原价) */
|
|
238
|
+
remainingCount: number;
|
|
239
|
+
/** 优惠金额 */
|
|
240
|
+
discountAmount: number;
|
|
241
|
+
/** 分组详情 */
|
|
242
|
+
groups: Array<{
|
|
243
|
+
products: Array<{
|
|
244
|
+
product_id: number;
|
|
245
|
+
product_variant_id: number;
|
|
246
|
+
originalPrice: number;
|
|
247
|
+
}>;
|
|
248
|
+
originalTotal: number;
|
|
249
|
+
discountedPrice: number;
|
|
250
|
+
}>;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* 买X送Y - 业务层计算结果
|
|
254
|
+
*
|
|
255
|
+
* 由业务层根据 matchedAction 计算后返回
|
|
256
|
+
*/
|
|
257
|
+
export interface BuyXGetYFreeResult {
|
|
258
|
+
/** 可送的赠品数量 */
|
|
259
|
+
freeCount: number;
|
|
260
|
+
/** 需要用户选择的赠品数量 */
|
|
261
|
+
pendingSelectCount: number;
|
|
262
|
+
/** 已选择的赠品 */
|
|
263
|
+
selectedGifts: ProductIdentifier[];
|
|
264
|
+
/** 可选的赠品列表 */
|
|
265
|
+
availableGifts: ProductIdentifier[];
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* 评估器输入
|
|
269
|
+
*/
|
|
270
|
+
export interface PromotionEvaluatorInput {
|
|
271
|
+
/** 商品列表 */
|
|
272
|
+
products: PromotionProduct[];
|
|
273
|
+
/** 策略配置列表(可选,如果不传则使用 setStrategyConfigs 设置的) */
|
|
274
|
+
strategyConfigs?: StrategyConfig[];
|
|
275
|
+
/** 渠道 */
|
|
276
|
+
channel?: string;
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* 单商品评估结果
|
|
280
|
+
*/
|
|
281
|
+
export interface ProductPromotionResult {
|
|
282
|
+
/** 商品信息 */
|
|
283
|
+
product: PromotionProduct;
|
|
284
|
+
/** 适用的促销列表 */
|
|
285
|
+
applicablePromotions: ApplicablePromotion[];
|
|
286
|
+
/** 是否有适用的促销 */
|
|
287
|
+
hasPromotion: boolean;
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* 适用的促销信息
|
|
291
|
+
*/
|
|
292
|
+
export interface ApplicablePromotion {
|
|
293
|
+
/** 策略ID */
|
|
294
|
+
strategyId: string;
|
|
295
|
+
/** 策略名称 */
|
|
296
|
+
strategyName: string | Record<string, string>;
|
|
297
|
+
/** Action 类型 */
|
|
298
|
+
actionType: string;
|
|
299
|
+
/** Action 详情 */
|
|
300
|
+
actionDetail: PromotionActionDetail;
|
|
301
|
+
/** 展示配置 */
|
|
302
|
+
display?: {
|
|
303
|
+
text: string | Record<string, string>;
|
|
304
|
+
type: string;
|
|
305
|
+
};
|
|
306
|
+
/** 原始策略配置 */
|
|
307
|
+
strategyConfig: StrategyConfig;
|
|
308
|
+
/** 匹配的 bundle 子商品索引(如果是 bundle 子商品匹配) */
|
|
309
|
+
matchedBundleIndex?: number;
|
|
310
|
+
/** 满足促销所需的商品数量 */
|
|
311
|
+
requiredQuantity?: number;
|
|
312
|
+
/** 参与促销的商品列表 */
|
|
313
|
+
eligibleProducts?: ProductIdentifier[];
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* 购物车评估结果
|
|
317
|
+
*/
|
|
318
|
+
export interface CartEvaluationResult {
|
|
319
|
+
/** 每个商品的促销结果 */
|
|
320
|
+
productResults: ProductPromotionResult[];
|
|
321
|
+
/** 购物车中所有适用的促销(去重) */
|
|
322
|
+
applicablePromotions: CartApplicablePromotion[];
|
|
323
|
+
/** 按促销分组的商品 */
|
|
324
|
+
promotionGroups: PromotionGroup[];
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* 购物车适用的促销
|
|
328
|
+
*/
|
|
329
|
+
export interface CartApplicablePromotion extends ApplicablePromotion {
|
|
330
|
+
/** 适用此促销的商品列表 */
|
|
331
|
+
applicableProducts: PromotionProduct[];
|
|
332
|
+
/** 适用商品的总数量 */
|
|
333
|
+
totalQuantity: number;
|
|
334
|
+
/** 适用商品的总金额 */
|
|
335
|
+
totalAmount: number;
|
|
336
|
+
/** 商品ID -> 匹配的 bundle 子商品索引映射 */
|
|
337
|
+
productMatchedBundleIndexMap?: Map<number, number | undefined>;
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* 促销分组
|
|
341
|
+
*/
|
|
342
|
+
export interface PromotionGroup {
|
|
343
|
+
/** 策略ID */
|
|
344
|
+
strategyId: string;
|
|
345
|
+
/** 策略名称 */
|
|
346
|
+
strategyName: string | Record<string, string>;
|
|
347
|
+
/** 策略元数据 */
|
|
348
|
+
strategyMetadata?: StrategyConfig['metadata'];
|
|
349
|
+
/** 原始策略配置 */
|
|
350
|
+
strategyConfig?: StrategyConfig;
|
|
351
|
+
/** Action 类型 */
|
|
352
|
+
actionType: string;
|
|
353
|
+
/** Action 详情 */
|
|
354
|
+
actionDetail: PromotionActionDetail;
|
|
355
|
+
/** 分组内的商品 */
|
|
356
|
+
products: PromotionProduct[];
|
|
357
|
+
/** 商品总数量 */
|
|
358
|
+
totalQuantity: number;
|
|
359
|
+
/** 商品总金额 */
|
|
360
|
+
totalAmount: number;
|
|
361
|
+
/** 商品ID -> 匹配的 bundle 子商品索引映射 */
|
|
362
|
+
productMatchedBundleIndexMap?: Map<number, number | undefined>;
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* 带定价的商品
|
|
366
|
+
*
|
|
367
|
+
* 在 PromotionProduct 基础上增加了均摊后的最终价格和促销信息
|
|
368
|
+
*/
|
|
369
|
+
export interface PricedProduct extends PromotionProduct {
|
|
370
|
+
/**
|
|
371
|
+
* 原始商品ID
|
|
372
|
+
* 当商品被拆分时,此字段记录拆分前的原始商品id
|
|
373
|
+
* 例如 A*5 拆成 A*4(参与促销)+ A*1(原价),A*4 的 originalId 指向原始 A 的 id
|
|
374
|
+
*/
|
|
375
|
+
originalId?: number;
|
|
376
|
+
/**
|
|
377
|
+
* 最终单价(均摊后)
|
|
378
|
+
* - 参与促销的商品:按原价比例均摊后的价格
|
|
379
|
+
* - 未参与促销的商品:等于 price
|
|
380
|
+
*/
|
|
381
|
+
finalPrice: number;
|
|
382
|
+
/**
|
|
383
|
+
* 应用的策略ID
|
|
384
|
+
* 如果商品参与了促销,记录对应的策略ID
|
|
385
|
+
*/
|
|
386
|
+
strategyId?: string;
|
|
387
|
+
/**
|
|
388
|
+
* 应用的策略元数据
|
|
389
|
+
* 如果商品参与了促销,记录对应策略的 metadata
|
|
390
|
+
*/
|
|
391
|
+
strategyMetadata?: StrategyConfig['metadata'];
|
|
392
|
+
/**
|
|
393
|
+
* 是否参与了促销
|
|
394
|
+
*/
|
|
395
|
+
inPromotion: boolean;
|
|
396
|
+
/**
|
|
397
|
+
* 是否为拆分出来的商品
|
|
398
|
+
* true 表示这是从原商品拆分出来的新记录
|
|
399
|
+
*/
|
|
400
|
+
isSplit?: boolean;
|
|
401
|
+
/**
|
|
402
|
+
* 匹配的 bundle 子商品索引
|
|
403
|
+
* 如果促销匹配的是 bundle 子商品而非主商品,记录对应的 bundle 索引
|
|
404
|
+
* undefined 表示匹配的是主商品
|
|
405
|
+
*/
|
|
406
|
+
matchedBundleIndex?: number;
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* 赠品信息
|
|
410
|
+
*
|
|
411
|
+
* 记录某个促销产生的赠品数量和可选赠品列表
|
|
412
|
+
*/
|
|
413
|
+
export interface GiftInfo {
|
|
414
|
+
/** 策略ID */
|
|
415
|
+
strategyId: string;
|
|
416
|
+
/** 策略名称 */
|
|
417
|
+
strategyName: string | Record<string, string>;
|
|
418
|
+
/** 可送赠品数量 */
|
|
419
|
+
giftCount: number;
|
|
420
|
+
/** 可选赠品列表 */
|
|
421
|
+
giftOptions: ProductIdentifier[];
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* 带定价的购物车评估结果
|
|
425
|
+
*
|
|
426
|
+
* evaluateCartWithPricing 方法的返回结果
|
|
427
|
+
*/
|
|
428
|
+
export interface CartWithPricingResult {
|
|
429
|
+
/**
|
|
430
|
+
* 处理后的商品数组
|
|
431
|
+
* - 包含所有商品(参与促销和未参与促销的)
|
|
432
|
+
* - 参与促销的商品可能被拆分(如 A*5 拆成 A*4 + A*1)
|
|
433
|
+
* - 每个商品都有 finalPrice 字段
|
|
434
|
+
*/
|
|
435
|
+
products: PricedProduct[];
|
|
436
|
+
/**
|
|
437
|
+
* 赠品信息列表
|
|
438
|
+
* 按促销分组,每个促销对应一个 GiftInfo
|
|
439
|
+
*/
|
|
440
|
+
gifts: GiftInfo[];
|
|
441
|
+
/**
|
|
442
|
+
* 总优惠金额
|
|
443
|
+
* 所有商品的原价总和 - 所有商品的 finalPrice 总和
|
|
444
|
+
*/
|
|
445
|
+
totalDiscount: number;
|
|
446
|
+
}
|
|
447
|
+
export {};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __export = (target, all) => {
|
|
6
|
+
for (var name in all)
|
|
7
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
8
|
+
};
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
18
|
+
|
|
19
|
+
// src/model/strategy/adapter/promotion/type.ts
|
|
20
|
+
var type_exports = {};
|
|
21
|
+
__export(type_exports, {
|
|
22
|
+
PROMOTION_ACTION_TYPES: () => PROMOTION_ACTION_TYPES,
|
|
23
|
+
PROMOTION_OPERATORS: () => PROMOTION_OPERATORS
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(type_exports);
|
|
26
|
+
var PROMOTION_ACTION_TYPES = {
|
|
27
|
+
/** X件Y元(每X件固定价格Y元,可累计) */
|
|
28
|
+
X_ITEMS_FOR_Y_PRICE: "X_ITEMS_FOR_Y_PRICE",
|
|
29
|
+
/** 买X送Y(买X件送Y件,可累计) */
|
|
30
|
+
BUY_X_GET_Y_FREE: "BUY_X_GET_Y_FREE",
|
|
31
|
+
/** 固定折扣 */
|
|
32
|
+
DISCOUNT_RATE: "DISCOUNT_RATE",
|
|
33
|
+
/** 固定减价 */
|
|
34
|
+
DEDUCT_AMOUNT: "DEDUCT_AMOUNT"
|
|
35
|
+
};
|
|
36
|
+
var PROMOTION_OPERATORS = {
|
|
37
|
+
/**
|
|
38
|
+
* 商品匹配运算符
|
|
39
|
+
*
|
|
40
|
+
* 匹配规则:
|
|
41
|
+
* - 首先匹配 product_id
|
|
42
|
+
* - 如果配置的 product_variant_id = 0,只需要 product_id 匹配
|
|
43
|
+
* - 如果配置的 product_variant_id != 0,还需要 product_variant_id 匹配
|
|
44
|
+
*/
|
|
45
|
+
PRODUCT_MATCH: "product_match"
|
|
46
|
+
};
|
|
47
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
48
|
+
0 && (module.exports = {
|
|
49
|
+
PROMOTION_ACTION_TYPES,
|
|
50
|
+
PROMOTION_OPERATORS
|
|
51
|
+
});
|
|
@@ -8,10 +8,12 @@ export declare class WalletPassEvaluator {
|
|
|
8
8
|
private engine;
|
|
9
9
|
private strategyConfigs;
|
|
10
10
|
private locale;
|
|
11
|
+
private otherParams;
|
|
11
12
|
constructor();
|
|
12
13
|
setStrategyConfigs(strategyConfigs: StrategyConfig[]): void;
|
|
13
14
|
getStrategyConfigs(): StrategyConfig[];
|
|
14
15
|
setLocale(locale: string): void;
|
|
16
|
+
setOtherParams(otherParams: any): void;
|
|
15
17
|
getText(key: string): string;
|
|
16
18
|
/**
|
|
17
19
|
* 搜索券的格式
|
|
@@ -42,19 +42,38 @@ var defaultStrategyMetadataCustom = {
|
|
|
42
42
|
allowCrossProduct: true,
|
|
43
43
|
applicableProductLimit: 0,
|
|
44
44
|
deductTaxAndFee: true,
|
|
45
|
-
maxPassesPerItem: 0
|
|
45
|
+
maxPassesPerItem: 0,
|
|
46
|
+
deductOptionPrice: false
|
|
46
47
|
};
|
|
48
|
+
var FIELD_REASON_CODE_MAP = {
|
|
49
|
+
orderTotalAmount: "order_total_amount_not_enough",
|
|
50
|
+
applicableProductTotalAmount: "applicable_product_total_amount_not_enough",
|
|
51
|
+
applicableProductCount: "applicable_product_count_not_enough"
|
|
52
|
+
};
|
|
53
|
+
var AMOUNT_FIELDS = /* @__PURE__ */ new Set([
|
|
54
|
+
"orderTotalAmount",
|
|
55
|
+
"applicableProductTotalAmount"
|
|
56
|
+
]);
|
|
57
|
+
function formatReason(template, value, currency = "") {
|
|
58
|
+
if (!template)
|
|
59
|
+
return template;
|
|
60
|
+
const v = value === void 0 || value === null ? "" : String(value);
|
|
61
|
+
const c = currency === void 0 || currency === null ? "" : String(currency);
|
|
62
|
+
return template.replace(/\$\{value\}/g, () => v).replace(/\{value\}/g, () => v).replace(/\{currency\}/g, () => c);
|
|
63
|
+
}
|
|
47
64
|
var WalletPassEvaluator = class {
|
|
48
65
|
constructor() {
|
|
49
66
|
this.strategyConfigs = [];
|
|
50
67
|
this.locale = "en";
|
|
68
|
+
this.otherParams = {};
|
|
51
69
|
this.engine = new import__.StrategyEngine({
|
|
52
70
|
debug: false,
|
|
53
71
|
enableTrace: false
|
|
54
72
|
});
|
|
55
73
|
}
|
|
56
74
|
setStrategyConfigs(strategyConfigs) {
|
|
57
|
-
|
|
75
|
+
const newStrategyConfigs = strategyConfigs.filter((item) => item.metadata.type === "wallet_pass");
|
|
76
|
+
this.strategyConfigs = newStrategyConfigs;
|
|
58
77
|
}
|
|
59
78
|
getStrategyConfigs() {
|
|
60
79
|
return this.strategyConfigs;
|
|
@@ -62,6 +81,9 @@ var WalletPassEvaluator = class {
|
|
|
62
81
|
setLocale(locale) {
|
|
63
82
|
this.locale = locale;
|
|
64
83
|
}
|
|
84
|
+
setOtherParams(otherParams) {
|
|
85
|
+
this.otherParams = otherParams;
|
|
86
|
+
}
|
|
65
87
|
getText(key) {
|
|
66
88
|
var _a, _b;
|
|
67
89
|
return (_b = (_a = import_locales.locales) == null ? void 0 : _a[this.locale]) == null ? void 0 : _b[key];
|
|
@@ -99,7 +121,7 @@ var WalletPassEvaluator = class {
|
|
|
99
121
|
* 评估可用的 vouchers
|
|
100
122
|
*/
|
|
101
123
|
evaluate(input) {
|
|
102
|
-
var _a, _b;
|
|
124
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
103
125
|
const { orderTotalAmount, products, vouchers, strategyConfigs } = input;
|
|
104
126
|
const productIds = products.map((p) => p.product_id);
|
|
105
127
|
const results = [];
|
|
@@ -174,7 +196,13 @@ var WalletPassEvaluator = class {
|
|
|
174
196
|
});
|
|
175
197
|
} else {
|
|
176
198
|
let reasonCode = "not_meet_the_required_conditions";
|
|
177
|
-
|
|
199
|
+
let thresholdValue = void 0;
|
|
200
|
+
const failedField = (_c = evaluationResult.matched) == null ? void 0 : _c.failedField;
|
|
201
|
+
const failedRule = (_e = (_d = evaluationResult.matched) == null ? void 0 : _d.failedRules) == null ? void 0 : _e[0];
|
|
202
|
+
if (failedField && FIELD_REASON_CODE_MAP[failedField]) {
|
|
203
|
+
reasonCode = FIELD_REASON_CODE_MAP[failedField];
|
|
204
|
+
thresholdValue = failedRule == null ? void 0 : failedRule.value;
|
|
205
|
+
} else if (evaluationResult.code) {
|
|
178
206
|
const codeMapping = {
|
|
179
207
|
INSUFFICIENT_AMOUNT: "not_meet_the_required_conditions",
|
|
180
208
|
USAGE_LIMIT_EXCEEDED: "usage_limit_reached",
|
|
@@ -184,6 +212,10 @@ var WalletPassEvaluator = class {
|
|
|
184
212
|
};
|
|
185
213
|
reasonCode = codeMapping[evaluationResult.code] || "not_meet_the_required_conditions";
|
|
186
214
|
}
|
|
215
|
+
const rawText = this.getText(reasonCode);
|
|
216
|
+
const currency = failedField && AMOUNT_FIELDS.has(failedField) ? ((_g = (_f = this.otherParams) == null ? void 0 : _f.getData) == null ? void 0 : _g.call(_f, "shop_symbol")) || "" : "";
|
|
217
|
+
console.log(currency, "currency12345");
|
|
218
|
+
const reason = thresholdValue !== void 0 ? formatReason(rawText, thresholdValue, currency) : rawText;
|
|
187
219
|
results.push({
|
|
188
220
|
voucher,
|
|
189
221
|
isApplicable: false,
|
|
@@ -191,7 +223,7 @@ var WalletPassEvaluator = class {
|
|
|
191
223
|
maxDeduction: 0,
|
|
192
224
|
deductTaxAndFee: false,
|
|
193
225
|
applicableProductIds: [],
|
|
194
|
-
reason
|
|
226
|
+
reason,
|
|
195
227
|
reasonCode,
|
|
196
228
|
strategyResult: evaluationResult
|
|
197
229
|
});
|
|
@@ -384,6 +416,7 @@ var WalletPassEvaluator = class {
|
|
|
384
416
|
* 计算适用商品的总金额和数量
|
|
385
417
|
*/
|
|
386
418
|
calculateApplicableProducts(products, applicableProductIds, deductTaxAndFee) {
|
|
419
|
+
console.log(products, "products1234");
|
|
387
420
|
let total = 0;
|
|
388
421
|
let count = 0;
|
|
389
422
|
if (applicableProductIds !== null && applicableProductIds.length === 0) {
|
|
@@ -29,7 +29,10 @@ var locales = {
|
|
|
29
29
|
"usage_limit_reached": "已达到使用次数上限",
|
|
30
30
|
"max_passes_per_item_reached": "该商品已达到卡券使用上限",
|
|
31
31
|
"not_available_for_this_channel": "当前渠道不可使用",
|
|
32
|
-
"not_valid_for_this_order_type": "当前订单类型不适用"
|
|
32
|
+
"not_valid_for_this_order_type": "当前订单类型不适用",
|
|
33
|
+
"order_total_amount_not_enough": "满 {currency}{value} 可用",
|
|
34
|
+
"applicable_product_total_amount_not_enough": "满 {currency}{value} 可用",
|
|
35
|
+
"applicable_product_count_not_enough": "至少 {value} 件"
|
|
33
36
|
},
|
|
34
37
|
"en": {
|
|
35
38
|
"not_meet_the_required_conditions": "Not meet the required conditions.",
|
|
@@ -37,7 +40,10 @@ var locales = {
|
|
|
37
40
|
"usage_limit_reached": "Usage limit reached.",
|
|
38
41
|
"max_passes_per_item_reached": "Max passes per item reached.",
|
|
39
42
|
"not_available_for_this_channel": "Not available for this channel.",
|
|
40
|
-
"not_valid_for_this_order_type": "Not valid for this order type."
|
|
43
|
+
"not_valid_for_this_order_type": "Not valid for this order type.",
|
|
44
|
+
"order_total_amount_not_enough": "Spend {currency}{value} to use",
|
|
45
|
+
"applicable_product_total_amount_not_enough": "Spend {currency}{value} to use",
|
|
46
|
+
"applicable_product_count_not_enough": "Min. Qty {value}"
|
|
41
47
|
},
|
|
42
48
|
"zh-HK": {
|
|
43
49
|
"not_meet_the_required_conditions": "未達使用條件",
|
|
@@ -45,7 +51,10 @@ var locales = {
|
|
|
45
51
|
"usage_limit_reached": "已達使用次數上限",
|
|
46
52
|
"max_passes_per_item_reached": "該商品已達卡券使用上限",
|
|
47
53
|
"not_available_for_this_channel": "當前渠道不可使用",
|
|
48
|
-
"not_valid_for_this_order_type": "當前訂單類型不適用"
|
|
54
|
+
"not_valid_for_this_order_type": "當前訂單類型不適用",
|
|
55
|
+
"order_total_amount_not_enough": "滿 {currency}{value} 可用",
|
|
56
|
+
"applicable_product_total_amount_not_enough": "滿 {currency}{value} 可用",
|
|
57
|
+
"applicable_product_count_not_enough": "至少 {value} 件"
|
|
49
58
|
}
|
|
50
59
|
};
|
|
51
60
|
// Annotate the CommonJS export names for ESM import in node:
|
|
@@ -41,8 +41,10 @@ export interface Voucher {
|
|
|
41
41
|
allowCrossProduct: boolean;
|
|
42
42
|
/** 可用商品数量上限 (仅多商品) 默认为0,表示不限制商品数量。 */
|
|
43
43
|
applicableProductLimit: number;
|
|
44
|
-
/**
|
|
44
|
+
/** 单订单行每单位可用同一 Wallet Pass 券次数;该行总券次上限 = maxPassesPerItem × 该行 quantity(按行唯一键区分)。0 表示不限制。 */
|
|
45
45
|
maxPassesPerItem: number;
|
|
46
|
+
/** 是否抵扣单规格价格 (Option Price),默认 false。开启后折扣范围包含 option 的 add_price */
|
|
47
|
+
deductOptionPrice: boolean;
|
|
46
48
|
};
|
|
47
49
|
}
|
|
48
50
|
/**
|
|
@@ -50,6 +52,10 @@ export interface Voucher {
|
|
|
50
52
|
*/
|
|
51
53
|
export interface Product {
|
|
52
54
|
product_id: number;
|
|
55
|
+
/** 订单明细 id,参与行唯一键兜底解析 */
|
|
56
|
+
id?: number;
|
|
57
|
+
/** 行级唯一串,参与行唯一键解析 */
|
|
58
|
+
product_unique_string?: string;
|
|
53
59
|
price: number;
|
|
54
60
|
quantity: number;
|
|
55
61
|
name?: string;
|
|
@@ -58,9 +64,19 @@ export interface Product {
|
|
|
58
64
|
main_product_original_price?: number;
|
|
59
65
|
/** 主商品折扣后金额,不包含套餐子商品 */
|
|
60
66
|
main_product_selling_price?: number;
|
|
67
|
+
/** 单规格(Option)列表 */
|
|
68
|
+
product_options?: {
|
|
69
|
+
id: number;
|
|
70
|
+
add_price: number | string;
|
|
71
|
+
num: number;
|
|
72
|
+
[key: string]: any;
|
|
73
|
+
}[];
|
|
61
74
|
/** 主商品税费 */
|
|
62
75
|
tax_fee: number;
|
|
63
76
|
metadata: {
|
|
77
|
+
/** 行唯一标识,优先用于 Wallet Pass 按行配额 */
|
|
78
|
+
product_unique?: string;
|
|
79
|
+
unique_identification_number?: string;
|
|
64
80
|
main_product_attached_bundle_tax_fee?: number;
|
|
65
81
|
main_product_attached_bundle_surcharge_fee?: number;
|
|
66
82
|
surcharge_rounding_remainder?: number;
|
|
@@ -132,8 +148,10 @@ export interface EvaluatorInput {
|
|
|
132
148
|
allowCrossProduct: boolean;
|
|
133
149
|
/** 可用商品数量上限 (仅多商品) 默认为0,表示不限制商品数量。 */
|
|
134
150
|
applicableProductLimit: number;
|
|
135
|
-
/**
|
|
151
|
+
/** 单订单行每单位可用同一 Wallet Pass 券次数;行总上限 = maxPassesPerItem × 该行 quantity(按行唯一键计)。0 表示不限制。 */
|
|
136
152
|
maxPassesPerItem: number;
|
|
153
|
+
/** 是否抵扣单规格价格 (Option Price),默认 false */
|
|
154
|
+
deductOptionPrice: boolean;
|
|
137
155
|
}>[];
|
|
138
156
|
}
|
|
139
157
|
/**
|