@pisell/pisellos 1.0.142 → 1.0.145
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/modules/Discount/index.d.ts +4 -2
- package/dist/modules/Discount/index.js +92 -4
- package/dist/modules/Discount/types.d.ts +7 -0
- package/dist/solution/ShopDiscount/index.d.ts +1 -0
- package/dist/solution/ShopDiscount/index.js +23 -18
- 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 +49 -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/modules/Discount/index.d.ts +4 -2
- package/lib/modules/Discount/index.js +62 -3
- package/lib/modules/Discount/types.d.ts +7 -0
- package/lib/solution/ShopDiscount/index.d.ts +1 -0
- package/lib/solution/ShopDiscount/index.js +4 -2
- package/package.json +2 -2
|
@@ -20,394 +20,19 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/model/strategy/index.ts
|
|
21
21
|
var strategy_exports = {};
|
|
22
22
|
__export(strategy_exports, {
|
|
23
|
-
StrategyEngine: () => StrategyEngine,
|
|
24
|
-
createStrategyEngine: () => createStrategyEngine,
|
|
25
|
-
evaluate: () => evaluate
|
|
23
|
+
StrategyEngine: () => import_engine.StrategyEngine,
|
|
24
|
+
createStrategyEngine: () => import_engine.createStrategyEngine,
|
|
25
|
+
evaluate: () => import_engine.evaluate
|
|
26
26
|
});
|
|
27
27
|
module.exports = __toCommonJS(strategy_exports);
|
|
28
|
-
var
|
|
28
|
+
var import_engine = require("./engine");
|
|
29
|
+
__reExport(strategy_exports, require("./type"), module.exports);
|
|
29
30
|
__reExport(strategy_exports, require("./adapter"), module.exports);
|
|
30
|
-
var StrategyEngine = class {
|
|
31
|
-
constructor(options = {}) {
|
|
32
|
-
this.options = {
|
|
33
|
-
debug: options.debug ?? false,
|
|
34
|
-
enableTrace: options.enableTrace ?? false,
|
|
35
|
-
operatorHandlers: options.operatorHandlers ?? {},
|
|
36
|
-
errorHandler: options.errorHandler ?? ((error) => console.error(error))
|
|
37
|
-
};
|
|
38
|
-
this.operatorHandlers = new Map(
|
|
39
|
-
Object.entries(this.options.operatorHandlers)
|
|
40
|
-
);
|
|
41
|
-
this.initializeBuiltInOperators();
|
|
42
|
-
}
|
|
43
|
-
/**
|
|
44
|
-
* 评估策略
|
|
45
|
-
*/
|
|
46
|
-
evaluate(config, context) {
|
|
47
|
-
var _a;
|
|
48
|
-
const startTime = Date.now();
|
|
49
|
-
const trace = {
|
|
50
|
-
steps: [],
|
|
51
|
-
duration: 0,
|
|
52
|
-
errors: []
|
|
53
|
-
};
|
|
54
|
-
try {
|
|
55
|
-
this.validateConfig(config);
|
|
56
|
-
if (this.options.enableTrace) {
|
|
57
|
-
trace.steps.push({
|
|
58
|
-
step: "validate_config",
|
|
59
|
-
status: "success",
|
|
60
|
-
duration: Date.now() - startTime
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
const evaluateStart = Date.now();
|
|
64
|
-
const evaluationResult = this.evaluateConditionGroup(
|
|
65
|
-
config.conditions,
|
|
66
|
-
context
|
|
67
|
-
);
|
|
68
|
-
if (this.options.enableTrace) {
|
|
69
|
-
trace.steps.push({
|
|
70
|
-
step: "evaluate_conditions",
|
|
71
|
-
status: "success",
|
|
72
|
-
duration: Date.now() - evaluateStart,
|
|
73
|
-
details: {
|
|
74
|
-
satisfied: evaluationResult.satisfied,
|
|
75
|
-
collectedActionIds: evaluationResult.actionIds
|
|
76
|
-
}
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
const actionStart = Date.now();
|
|
80
|
-
const matchedActions = this.getActionsByIds(
|
|
81
|
-
evaluationResult.actionIds,
|
|
82
|
-
config.actions
|
|
83
|
-
);
|
|
84
|
-
if (this.options.enableTrace) {
|
|
85
|
-
trace.steps.push({
|
|
86
|
-
step: "get_actions",
|
|
87
|
-
status: "success",
|
|
88
|
-
duration: Date.now() - actionStart,
|
|
89
|
-
details: { matchedCount: matchedActions.length }
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
const sortStart = Date.now();
|
|
93
|
-
const sortedActions = this.sortActionsByPriority(matchedActions);
|
|
94
|
-
if (this.options.enableTrace) {
|
|
95
|
-
trace.steps.push({
|
|
96
|
-
step: "sort_actions",
|
|
97
|
-
status: "success",
|
|
98
|
-
duration: Date.now() - sortStart
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
const applicable = evaluationResult.satisfied;
|
|
102
|
-
const result = {
|
|
103
|
-
success: true,
|
|
104
|
-
applicable,
|
|
105
|
-
code: applicable ? import_type.SUCCESS_CODES.SUCCESS : import_type.NOT_APPLICABLE_CODES.CONDITION_NOT_MET,
|
|
106
|
-
message: applicable ? "Strategy is applicable" : "Conditions not met",
|
|
107
|
-
matched: {
|
|
108
|
-
conditions: applicable,
|
|
109
|
-
actionIds: evaluationResult.actionIds,
|
|
110
|
-
details: {}
|
|
111
|
-
},
|
|
112
|
-
matchedActions: sortedActions,
|
|
113
|
-
outputs: {},
|
|
114
|
-
config
|
|
115
|
-
};
|
|
116
|
-
trace.duration = Date.now() - startTime;
|
|
117
|
-
if (this.options.enableTrace) {
|
|
118
|
-
result.trace = trace;
|
|
119
|
-
}
|
|
120
|
-
return result;
|
|
121
|
-
} catch (error) {
|
|
122
|
-
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
123
|
-
if (this.options.enableTrace) {
|
|
124
|
-
(_a = trace.errors) == null ? void 0 : _a.push({
|
|
125
|
-
step: "evaluation",
|
|
126
|
-
error: errorMessage,
|
|
127
|
-
timestamp: Date.now()
|
|
128
|
-
});
|
|
129
|
-
trace.duration = Date.now() - startTime;
|
|
130
|
-
}
|
|
131
|
-
this.options.errorHandler(error, context);
|
|
132
|
-
return {
|
|
133
|
-
success: false,
|
|
134
|
-
applicable: false,
|
|
135
|
-
code: import_type.ERROR_CODES.EVALUATION_ERROR,
|
|
136
|
-
message: errorMessage,
|
|
137
|
-
matched: {
|
|
138
|
-
conditions: false,
|
|
139
|
-
actionIds: [],
|
|
140
|
-
details: {}
|
|
141
|
-
},
|
|
142
|
-
matchedActions: [],
|
|
143
|
-
outputs: {},
|
|
144
|
-
trace: this.options.enableTrace ? trace : void 0,
|
|
145
|
-
config
|
|
146
|
-
};
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
/**
|
|
150
|
-
* 递归评估条件组
|
|
151
|
-
*
|
|
152
|
-
* @param group 条件组
|
|
153
|
-
* @param context 运行时上下文
|
|
154
|
-
* @returns 评估结果对象,包含条件是否满足和收集到的 actionIds
|
|
155
|
-
*/
|
|
156
|
-
evaluateConditionGroup(group, context) {
|
|
157
|
-
const collectedActionIds = [];
|
|
158
|
-
const isCurrentLayerSatisfied = this.evaluateGroupLogic(group, context);
|
|
159
|
-
if (isCurrentLayerSatisfied) {
|
|
160
|
-
collectedActionIds.push(...group.actionIds);
|
|
161
|
-
for (const rule of group.rules) {
|
|
162
|
-
if (this.isConditionGroup(rule)) {
|
|
163
|
-
const nestedResult = this.evaluateConditionGroup(rule, context);
|
|
164
|
-
collectedActionIds.push(...nestedResult.actionIds);
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
return {
|
|
169
|
-
satisfied: isCurrentLayerSatisfied,
|
|
170
|
-
actionIds: collectedActionIds
|
|
171
|
-
};
|
|
172
|
-
}
|
|
173
|
-
/**
|
|
174
|
-
* 评估条件组的逻辑运算
|
|
175
|
-
*/
|
|
176
|
-
evaluateGroupLogic(group, context) {
|
|
177
|
-
const { operator, rules } = group;
|
|
178
|
-
switch (operator) {
|
|
179
|
-
case "and":
|
|
180
|
-
return rules.every((rule) => this.evaluateRule(rule, context));
|
|
181
|
-
case "or":
|
|
182
|
-
return rules.some((rule) => this.evaluateRule(rule, context));
|
|
183
|
-
case "not":
|
|
184
|
-
if (rules.length === 0)
|
|
185
|
-
return false;
|
|
186
|
-
return !this.evaluateRule(rules[0], context);
|
|
187
|
-
default:
|
|
188
|
-
throw new Error(`Unknown operator: ${operator}`);
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
/**
|
|
192
|
-
* 评估单个规则
|
|
193
|
-
*/
|
|
194
|
-
evaluateRule(rule, context) {
|
|
195
|
-
if (this.isConditionGroup(rule)) {
|
|
196
|
-
return this.evaluateGroupLogic(rule, context);
|
|
197
|
-
}
|
|
198
|
-
const conditionRule = rule;
|
|
199
|
-
if (conditionRule.type === "code" && conditionRule.code) {
|
|
200
|
-
return this.evaluateCodeCondition(conditionRule.code, context);
|
|
201
|
-
}
|
|
202
|
-
return this.evaluateOperatorCondition(conditionRule, context);
|
|
203
|
-
}
|
|
204
|
-
/**
|
|
205
|
-
* 评估代码模式条件
|
|
206
|
-
*/
|
|
207
|
-
evaluateCodeCondition(code, context) {
|
|
208
|
-
try {
|
|
209
|
-
const { entities, attributes, metadata } = context;
|
|
210
|
-
const evalFunc = new Function(
|
|
211
|
-
"entities",
|
|
212
|
-
"attributes",
|
|
213
|
-
"metadata",
|
|
214
|
-
`return (${code})`
|
|
215
|
-
);
|
|
216
|
-
const result = evalFunc(entities, attributes, metadata);
|
|
217
|
-
return Boolean(result);
|
|
218
|
-
} catch (error) {
|
|
219
|
-
if (this.options.debug) {
|
|
220
|
-
console.error("Code evaluation error:", error);
|
|
221
|
-
}
|
|
222
|
-
return false;
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
/**
|
|
226
|
-
* 评估运算符模式条件
|
|
227
|
-
*/
|
|
228
|
-
evaluateOperatorCondition(rule, context) {
|
|
229
|
-
const { field, operator, value } = rule;
|
|
230
|
-
if (!field || !operator) {
|
|
231
|
-
return false;
|
|
232
|
-
}
|
|
233
|
-
const fieldValue = this.getFieldValue(field, context);
|
|
234
|
-
const handler = this.operatorHandlers.get(operator);
|
|
235
|
-
if (handler) {
|
|
236
|
-
return handler(fieldValue, value, rule);
|
|
237
|
-
}
|
|
238
|
-
return this.evaluateBuiltInOperator(fieldValue, operator, value);
|
|
239
|
-
}
|
|
240
|
-
/**
|
|
241
|
-
* 获取字段值
|
|
242
|
-
*/
|
|
243
|
-
getFieldValue(field, context) {
|
|
244
|
-
const path = field.split(".");
|
|
245
|
-
let value = context.attributes;
|
|
246
|
-
for (const key of path) {
|
|
247
|
-
if (value && typeof value === "object" && key in value) {
|
|
248
|
-
value = value[key];
|
|
249
|
-
} else {
|
|
250
|
-
return void 0;
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
return value;
|
|
254
|
-
}
|
|
255
|
-
/**
|
|
256
|
-
* 评估内置运算符
|
|
257
|
-
*/
|
|
258
|
-
evaluateBuiltInOperator(fieldValue, operator, compareValue) {
|
|
259
|
-
switch (operator) {
|
|
260
|
-
case "=":
|
|
261
|
-
case "==":
|
|
262
|
-
return fieldValue == compareValue;
|
|
263
|
-
case "!=":
|
|
264
|
-
return fieldValue != compareValue;
|
|
265
|
-
case ">":
|
|
266
|
-
return fieldValue > compareValue;
|
|
267
|
-
case ">=":
|
|
268
|
-
return fieldValue >= compareValue;
|
|
269
|
-
case "<":
|
|
270
|
-
return fieldValue < compareValue;
|
|
271
|
-
case "<=":
|
|
272
|
-
return fieldValue <= compareValue;
|
|
273
|
-
case "in":
|
|
274
|
-
return Array.isArray(compareValue) && compareValue.includes(fieldValue);
|
|
275
|
-
case "not_in":
|
|
276
|
-
return Array.isArray(compareValue) && !compareValue.includes(fieldValue);
|
|
277
|
-
case "contains":
|
|
278
|
-
if (Array.isArray(fieldValue)) {
|
|
279
|
-
return fieldValue.includes(compareValue);
|
|
280
|
-
}
|
|
281
|
-
if (typeof fieldValue === "string") {
|
|
282
|
-
return fieldValue.includes(compareValue);
|
|
283
|
-
}
|
|
284
|
-
return false;
|
|
285
|
-
case "not_contains":
|
|
286
|
-
if (Array.isArray(fieldValue)) {
|
|
287
|
-
return !fieldValue.includes(compareValue);
|
|
288
|
-
}
|
|
289
|
-
if (typeof fieldValue === "string") {
|
|
290
|
-
return !fieldValue.includes(compareValue);
|
|
291
|
-
}
|
|
292
|
-
return true;
|
|
293
|
-
case "starts_with":
|
|
294
|
-
return typeof fieldValue === "string" && fieldValue.startsWith(compareValue);
|
|
295
|
-
case "ends_with":
|
|
296
|
-
return typeof fieldValue === "string" && fieldValue.endsWith(compareValue);
|
|
297
|
-
case "regex":
|
|
298
|
-
return typeof fieldValue === "string" && new RegExp(compareValue).test(fieldValue);
|
|
299
|
-
case "between":
|
|
300
|
-
if (!Array.isArray(compareValue) || compareValue.length !== 2)
|
|
301
|
-
return false;
|
|
302
|
-
return fieldValue >= compareValue[0] && fieldValue <= compareValue[1];
|
|
303
|
-
case "is_null":
|
|
304
|
-
return fieldValue === null || fieldValue === void 0;
|
|
305
|
-
case "is_not_null":
|
|
306
|
-
return fieldValue !== null && fieldValue !== void 0;
|
|
307
|
-
case "is_empty":
|
|
308
|
-
if (Array.isArray(fieldValue))
|
|
309
|
-
return fieldValue.length === 0;
|
|
310
|
-
if (typeof fieldValue === "string")
|
|
311
|
-
return fieldValue.length === 0;
|
|
312
|
-
if (typeof fieldValue === "object")
|
|
313
|
-
return Object.keys(fieldValue).length === 0;
|
|
314
|
-
return !fieldValue;
|
|
315
|
-
case "is_not_empty":
|
|
316
|
-
if (Array.isArray(fieldValue))
|
|
317
|
-
return fieldValue.length > 0;
|
|
318
|
-
if (typeof fieldValue === "string")
|
|
319
|
-
return fieldValue.length > 0;
|
|
320
|
-
if (typeof fieldValue === "object")
|
|
321
|
-
return Object.keys(fieldValue).length > 0;
|
|
322
|
-
return Boolean(fieldValue);
|
|
323
|
-
default:
|
|
324
|
-
throw new Error(`Unsupported operator: ${operator}`);
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
/**
|
|
328
|
-
* 根据 actionIds 获取 ActionEffect 对象
|
|
329
|
-
*/
|
|
330
|
-
getActionsByIds(actionIds, actions) {
|
|
331
|
-
const matchedActions = [];
|
|
332
|
-
const actionMap = new Map(actions.map((action) => [action.id, action]));
|
|
333
|
-
for (const id of actionIds) {
|
|
334
|
-
const action = actionMap.get(id);
|
|
335
|
-
if (action) {
|
|
336
|
-
matchedActions.push(action);
|
|
337
|
-
} else {
|
|
338
|
-
if (this.options.debug) {
|
|
339
|
-
console.warn(`Action with id "${id}" not found in actions array`);
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
return matchedActions;
|
|
344
|
-
}
|
|
345
|
-
/**
|
|
346
|
-
* 按 priority 排序(降序)
|
|
347
|
-
*/
|
|
348
|
-
sortActionsByPriority(actions) {
|
|
349
|
-
return [...actions].sort((a, b) => {
|
|
350
|
-
const priorityA = a.priority ?? 0;
|
|
351
|
-
const priorityB = b.priority ?? 0;
|
|
352
|
-
return priorityB - priorityA;
|
|
353
|
-
});
|
|
354
|
-
}
|
|
355
|
-
/**
|
|
356
|
-
* 判断是否为条件组
|
|
357
|
-
*/
|
|
358
|
-
isConditionGroup(rule) {
|
|
359
|
-
return "operator" in rule && "rules" in rule && Array.isArray(rule.rules);
|
|
360
|
-
}
|
|
361
|
-
/**
|
|
362
|
-
* 验证配置
|
|
363
|
-
*/
|
|
364
|
-
validateConfig(config) {
|
|
365
|
-
if (!config) {
|
|
366
|
-
throw new Error("Strategy config is required");
|
|
367
|
-
}
|
|
368
|
-
if (!config.metadata || !config.metadata.id) {
|
|
369
|
-
throw new Error("Strategy metadata.id is required");
|
|
370
|
-
}
|
|
371
|
-
if (!config.conditions) {
|
|
372
|
-
throw new Error("Strategy conditions is required");
|
|
373
|
-
}
|
|
374
|
-
if (!Array.isArray(config.actions)) {
|
|
375
|
-
throw new Error("Strategy actions must be an array");
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
/**
|
|
379
|
-
* 初始化内置运算符处理器
|
|
380
|
-
*/
|
|
381
|
-
initializeBuiltInOperators() {
|
|
382
|
-
}
|
|
383
|
-
/**
|
|
384
|
-
* 注册自定义运算符
|
|
385
|
-
*/
|
|
386
|
-
registerOperator(operator, handler) {
|
|
387
|
-
this.operatorHandlers.set(operator, handler);
|
|
388
|
-
}
|
|
389
|
-
/**
|
|
390
|
-
* 获取调试信息
|
|
391
|
-
*/
|
|
392
|
-
getDebugInfo() {
|
|
393
|
-
return {
|
|
394
|
-
debug: this.options.debug,
|
|
395
|
-
enableTrace: this.options.enableTrace,
|
|
396
|
-
registeredOperators: Array.from(this.operatorHandlers.keys())
|
|
397
|
-
};
|
|
398
|
-
}
|
|
399
|
-
};
|
|
400
|
-
function createStrategyEngine(options) {
|
|
401
|
-
return new StrategyEngine(options);
|
|
402
|
-
}
|
|
403
|
-
function evaluate(config, context) {
|
|
404
|
-
const engine = new StrategyEngine();
|
|
405
|
-
return engine.evaluate(config, context);
|
|
406
|
-
}
|
|
407
31
|
// Annotate the CommonJS export names for ESM import in node:
|
|
408
32
|
0 && (module.exports = {
|
|
409
33
|
StrategyEngine,
|
|
410
34
|
createStrategyEngine,
|
|
411
35
|
evaluate,
|
|
36
|
+
...require("./type"),
|
|
412
37
|
...require("./adapter")
|
|
413
38
|
});
|
|
@@ -165,6 +165,23 @@ export interface MatchedInfo {
|
|
|
165
165
|
actionIds: string[];
|
|
166
166
|
/** 详细匹配信息 */
|
|
167
167
|
details: Record<string, any>;
|
|
168
|
+
/** 顶层第一个失败的 field(仅在 conditions=false 时存在,便于直接消费) */
|
|
169
|
+
failedField?: string;
|
|
170
|
+
/** 失败的原子规则列表(仅在 conditions=false 时存在;or 全失败时可能有多条) */
|
|
171
|
+
failedRules?: FailedRuleInfo[];
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* 失败规则信息
|
|
175
|
+
*/
|
|
176
|
+
export interface FailedRuleInfo {
|
|
177
|
+
/** 字段名 */
|
|
178
|
+
field?: string;
|
|
179
|
+
/** 运算符 */
|
|
180
|
+
operator?: string;
|
|
181
|
+
/** 配置的比较值(阈值) */
|
|
182
|
+
value?: any;
|
|
183
|
+
/** 实际取到的字段值 */
|
|
184
|
+
actualValue?: any;
|
|
168
185
|
}
|
|
169
186
|
/**
|
|
170
187
|
* 执行轨迹
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Module, PisellCore, ModuleOptions } from '../../types';
|
|
2
2
|
import { BaseModule } from '../BaseModule';
|
|
3
|
-
import { Discount, DiscountModuleAPI } from './types';
|
|
3
|
+
import { Discount, DiscountModuleAPI, DiscountWithReason } from './types';
|
|
4
4
|
export declare class DiscountModule extends BaseModule implements Module, DiscountModuleAPI {
|
|
5
5
|
protected defaultName: string;
|
|
6
6
|
protected defaultVersion: string;
|
|
@@ -24,10 +24,12 @@ export declare class DiscountModule extends BaseModule implements Module, Discou
|
|
|
24
24
|
with_wallet_pass_holder: 0 | 1;
|
|
25
25
|
request_timezone: string;
|
|
26
26
|
}): Promise<Discount[]>;
|
|
27
|
-
batchSearch(code: string, customerId?: number): Promise<
|
|
27
|
+
batchSearch(code: string, customerId?: number): Promise<DiscountWithReason>;
|
|
28
28
|
batchSearchByProductIds(productIds: number[]): Promise<any>;
|
|
29
29
|
filterEnabledDiscountList(discountList: Discount[]): Discount[];
|
|
30
30
|
private checkUsageCreditsLimit;
|
|
31
|
+
filterEnabledDiscountListWithReason(discountList: Discount[]): DiscountWithReason;
|
|
32
|
+
private checkUsageCreditsLimitWithFailKey;
|
|
31
33
|
uniqueByProductId(discountList: Discount[]): Discount[];
|
|
32
34
|
filterDiscountListByProductIds(discountList: Discount[], productIds: number[]): Discount[];
|
|
33
35
|
calcDiscountApplicableProductTotalPrice(discount: Discount): number | undefined;
|
|
@@ -107,15 +107,16 @@ var DiscountModule = class extends import_BaseModule.BaseModule {
|
|
|
107
107
|
code,
|
|
108
108
|
translate_flag: 1,
|
|
109
109
|
tags: ["good_pass", "product_discount_card"],
|
|
110
|
-
available: 1,
|
|
110
|
+
// available: 1,
|
|
111
111
|
relation_product: 1,
|
|
112
112
|
with: ["extensionData", "customScheduleSnapshot", "holder.detail"],
|
|
113
113
|
order_behavior_count: 1,
|
|
114
114
|
order_behavior_count_customer_id: customerId || 1,
|
|
115
|
+
unified_status_info_flag: 1,
|
|
115
116
|
request_timezone: Intl.DateTimeFormat().resolvedOptions().timeZone
|
|
116
117
|
});
|
|
117
|
-
const
|
|
118
|
-
return
|
|
118
|
+
const resultDiscountWithReason = this.filterEnabledDiscountListWithReason((result == null ? void 0 : result.data) || []) || [];
|
|
119
|
+
return resultDiscountWithReason;
|
|
119
120
|
}
|
|
120
121
|
async batchSearchByProductIds(productIds) {
|
|
121
122
|
const result = await this.request.get(`/machinecode/batch-search`, {
|
|
@@ -170,6 +171,64 @@ var DiscountModule = class extends import_BaseModule.BaseModule {
|
|
|
170
171
|
}
|
|
171
172
|
return true;
|
|
172
173
|
}
|
|
174
|
+
// 过滤启用且可用的优惠券,并返回校验失败原因
|
|
175
|
+
filterEnabledDiscountListWithReason(discountList) {
|
|
176
|
+
let lists = discountList.filter((discount) => discount.limit_status === "enable").filter((discount) => {
|
|
177
|
+
const remaining = new import_decimal.default((discount == null ? void 0 : discount.par_value) || 0).minus(new import_decimal.default((discount == null ? void 0 : discount.used_par_value) || 0)).greaterThan(0);
|
|
178
|
+
return remaining;
|
|
179
|
+
}).map((discount) => {
|
|
180
|
+
const limitFailKey = this.checkUsageCreditsLimitWithFailKey(discount);
|
|
181
|
+
return {
|
|
182
|
+
...discount,
|
|
183
|
+
usageLimitFailKey: limitFailKey
|
|
184
|
+
};
|
|
185
|
+
});
|
|
186
|
+
const availableLists = lists.filter((item) => !item.usageLimitFailKey && item.unified_available_status !== 0);
|
|
187
|
+
const currentFailDiscount = lists.find((item) => item.usageLimitFailKey);
|
|
188
|
+
return {
|
|
189
|
+
discountList: availableLists,
|
|
190
|
+
unavailableReasonKey: currentFailDiscount == null ? void 0 : currentFailDiscount.usageLimitFailKey
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
// 检查使用次数限制,并返回校验失败原因
|
|
194
|
+
checkUsageCreditsLimitWithFailKey(discount) {
|
|
195
|
+
if (!discount.extension_data || discount.extension_data.length === 0) {
|
|
196
|
+
return null;
|
|
197
|
+
}
|
|
198
|
+
const usageCreditsData = discount.extension_data.find(
|
|
199
|
+
(data) => data.field_key === "usage_credits"
|
|
200
|
+
);
|
|
201
|
+
if (!usageCreditsData) {
|
|
202
|
+
return null;
|
|
203
|
+
}
|
|
204
|
+
const value = usageCreditsData.value;
|
|
205
|
+
if (value.total_credits && value.total_credits > 0) {
|
|
206
|
+
if ((discount.total_order_behavior_count || 0) >= value.total_credits) {
|
|
207
|
+
return "total_credits";
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
if (value.per_user_limit && value.per_user_limit > 0) {
|
|
211
|
+
if ((discount.customer_order_behavior_count || 0) >= value.per_user_limit) {
|
|
212
|
+
return "per_user_limit";
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
if (value.max_per_day && value.max_per_day > 0) {
|
|
216
|
+
if ((discount.today_order_behavior_count || 0) >= value.max_per_day) {
|
|
217
|
+
return "max_per_day";
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
if (value.max_per_week && value.max_per_week > 0) {
|
|
221
|
+
if ((discount.week_order_behavior_count || 0) >= value.max_per_week) {
|
|
222
|
+
return "max_per_week";
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
if (value.max_per_month && value.max_per_month > 0) {
|
|
226
|
+
if ((discount.month_order_behavior_count || 0) >= value.max_per_month) {
|
|
227
|
+
return "max_per_month";
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return null;
|
|
231
|
+
}
|
|
173
232
|
// 根据productIds去重
|
|
174
233
|
uniqueByProductId(discountList) {
|
|
175
234
|
return (0, import_utils.uniqueById)(discountList, "product_id");
|
|
@@ -63,6 +63,11 @@ interface UsageCreditsValue {
|
|
|
63
63
|
max_per_week: number;
|
|
64
64
|
max_per_month: number;
|
|
65
65
|
}
|
|
66
|
+
export type DiscountFilterRejectKey = 'total_credits' | 'per_user_limit' | 'max_per_day' | 'max_per_week' | 'max_per_month';
|
|
67
|
+
export interface DiscountWithReason {
|
|
68
|
+
discountList: Discount[];
|
|
69
|
+
unavailableReasonKey: DiscountFilterRejectKey | null;
|
|
70
|
+
}
|
|
66
71
|
interface ExtensionData {
|
|
67
72
|
id: number;
|
|
68
73
|
shop_id: number;
|
|
@@ -147,6 +152,8 @@ export interface Discount {
|
|
|
147
152
|
start_date?: string;
|
|
148
153
|
start_time?: string;
|
|
149
154
|
discount_rule_uncheck_flag?: boolean;
|
|
155
|
+
usageLimitFailKey?: string | null;
|
|
156
|
+
unified_available_status?: number | undefined | null;
|
|
150
157
|
}
|
|
151
158
|
export interface DiscountState {
|
|
152
159
|
discountList: Discount[];
|
|
@@ -46,6 +46,7 @@ export declare class ShopDiscountImpl extends BaseModule implements Module {
|
|
|
46
46
|
discountList: Discount[];
|
|
47
47
|
type: "server" | "clientCalc";
|
|
48
48
|
unavailableReason?: UnavailableReason;
|
|
49
|
+
unavailableReasonKey?: string | null;
|
|
49
50
|
}>;
|
|
50
51
|
calcDiscountApplicableProductTotalPrice(discount: Discount): number | undefined;
|
|
51
52
|
private getCustomer;
|
|
@@ -282,7 +282,8 @@ var ShopDiscountImpl = class extends import_BaseModule.BaseModule {
|
|
|
282
282
|
async scanCode(code, customerId) {
|
|
283
283
|
var _a, _b, _c;
|
|
284
284
|
try {
|
|
285
|
-
const
|
|
285
|
+
const resultDiscountWithReason = await ((_a = this.store.discount) == null ? void 0 : _a.batchSearch(code, customerId)) || { discountList: [], unavailableReasonKey: null };
|
|
286
|
+
const { discountList: resultDiscountList, unavailableReasonKey } = resultDiscountWithReason;
|
|
286
287
|
const rulesModule = this.store.rules;
|
|
287
288
|
if (!rulesModule) {
|
|
288
289
|
return {
|
|
@@ -298,7 +299,8 @@ var ShopDiscountImpl = class extends import_BaseModule.BaseModule {
|
|
|
298
299
|
type: "server",
|
|
299
300
|
isAvailable: false,
|
|
300
301
|
productList: this.store.productList || [],
|
|
301
|
-
discountList: this.getDiscountList()
|
|
302
|
+
discountList: this.getDiscountList(),
|
|
303
|
+
unavailableReasonKey
|
|
302
304
|
};
|
|
303
305
|
}
|
|
304
306
|
const withScanList = resultDiscountList.map((item) => {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"private": false,
|
|
3
3
|
"name": "@pisell/pisellos",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.145",
|
|
5
5
|
"description": "一个可扩展的前端模块化SDK框架,支持插件系统",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"format": "prettier --write \"src/**/*.{ts,tsx,js,jsx,json,md}\"",
|
|
19
19
|
"changeset": "changeset",
|
|
20
20
|
"version": "changeset version",
|
|
21
|
-
"release": "changeset publish
|
|
21
|
+
"release": "changeset publish && npm run sync && git push",
|
|
22
22
|
"example": "npx parcel-bundler examples/index.html",
|
|
23
23
|
"docs": "typedoc --out docs src/index.ts",
|
|
24
24
|
"sync": "node sync.js",
|