@pisell/pisellos
Version:
一个可扩展的前端模块化SDK框架,支持插件系统
451 lines (449 loc) • 14 kB
JavaScript
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/model/strategy/engine.ts
var engine_exports = {};
__export(engine_exports, {
StrategyEngine: () => StrategyEngine,
createStrategyEngine: () => createStrategyEngine,
evaluate: () => evaluate
});
module.exports = __toCommonJS(engine_exports);
var import_type = require("./type");
var StrategyEngine = class {
constructor(options = {}) {
this.options = {
debug: options.debug ?? false,
enableTrace: options.enableTrace ?? false,
operatorHandlers: options.operatorHandlers ?? {},
errorHandler: options.errorHandler ?? ((error) => console.error(error))
};
this.operatorHandlers = new Map(
Object.entries(this.options.operatorHandlers)
);
this.initializeBuiltInOperators();
}
/**
* 评估策略
*/
evaluate(config, context) {
var _a;
const startTime = Date.now();
const trace = {
steps: [],
duration: 0,
errors: []
};
try {
this.validateConfig(config);
if (this.options.enableTrace) {
trace.steps.push({
step: "validate_config",
status: "success",
duration: Date.now() - startTime
});
}
const evaluateStart = Date.now();
const evaluationResult = this.evaluateConditionGroup(
config.conditions,
context
);
if (this.options.enableTrace) {
trace.steps.push({
step: "evaluate_conditions",
status: "success",
duration: Date.now() - evaluateStart,
details: {
satisfied: evaluationResult.satisfied,
collectedActionIds: evaluationResult.actionIds
}
});
}
const actionStart = Date.now();
const matchedActions = this.getActionsByIds(
evaluationResult.actionIds,
config.actions
);
if (this.options.enableTrace) {
trace.steps.push({
step: "get_actions",
status: "success",
duration: Date.now() - actionStart,
details: { matchedCount: matchedActions.length }
});
}
const sortStart = Date.now();
const sortedActions = this.sortActionsByPriority(matchedActions);
if (this.options.enableTrace) {
trace.steps.push({
step: "sort_actions",
status: "success",
duration: Date.now() - sortStart
});
}
const applicable = evaluationResult.satisfied;
const result = {
success: true,
applicable,
code: applicable ? import_type.SUCCESS_CODES.SUCCESS : import_type.NOT_APPLICABLE_CODES.CONDITION_NOT_MET,
message: applicable ? "Strategy is applicable" : "Conditions not met",
matched: {
conditions: applicable,
actionIds: evaluationResult.actionIds,
details: {}
},
matchedActions: sortedActions,
outputs: {},
config
};
trace.duration = Date.now() - startTime;
if (this.options.enableTrace) {
result.trace = trace;
}
return result;
} catch (error) {
const errorMessage = error instanceof Error ? error.message : "Unknown error";
if (this.options.enableTrace) {
(_a = trace.errors) == null ? void 0 : _a.push({
step: "evaluation",
error: errorMessage,
timestamp: Date.now()
});
trace.duration = Date.now() - startTime;
}
this.options.errorHandler(error, context);
return {
success: false,
applicable: false,
code: import_type.ERROR_CODES.EVALUATION_ERROR,
message: errorMessage,
matched: {
conditions: false,
actionIds: [],
details: {}
},
matchedActions: [],
outputs: {},
trace: this.options.enableTrace ? trace : void 0,
config
};
}
}
/**
* 递归评估条件组
*
* @param group 条件组
* @param context 运行时上下文
* @returns 评估结果对象,包含条件是否满足和收集到的 actionIds
*/
evaluateConditionGroup(group, context) {
const collectedActionIds = [];
const isCurrentLayerSatisfied = this.evaluateGroupLogic(group, context);
if (isCurrentLayerSatisfied) {
collectedActionIds.push(...group.actionIds);
for (const rule of group.rules) {
if (this.isConditionGroup(rule)) {
const nestedResult = this.evaluateConditionGroup(rule, context);
collectedActionIds.push(...nestedResult.actionIds);
}
}
}
return {
satisfied: isCurrentLayerSatisfied,
actionIds: collectedActionIds
};
}
/**
* 评估条件组的逻辑运算
*/
evaluateGroupLogic(group, context) {
const { operator, rules } = group;
switch (operator) {
case "and":
return rules.every((rule) => this.evaluateRule(rule, context));
case "or":
return rules.some((rule) => this.evaluateRule(rule, context));
case "not":
if (rules.length === 0)
return false;
return !this.evaluateRule(rules[0], context);
default:
throw new Error(`Unknown operator: ${operator}`);
}
}
/**
* 评估单个规则
*/
evaluateRule(rule, context) {
if (this.isConditionGroup(rule)) {
return this.evaluateGroupLogic(rule, context);
}
const conditionRule = rule;
if (conditionRule.type === "code" && conditionRule.code) {
return this.evaluateCodeCondition(conditionRule.code, context);
}
return this.evaluateOperatorCondition(conditionRule, context);
}
/**
* 评估代码模式条件
*/
evaluateCodeCondition(code, context) {
try {
const { entities, attributes, metadata } = context;
const evalFunc = new Function(
"entities",
"attributes",
"metadata",
`return (${code})`
);
const result = evalFunc(entities, attributes, metadata);
return Boolean(result);
} catch (error) {
if (this.options.debug) {
console.error("Code evaluation error:", error);
}
return false;
}
}
/**
* 评估运算符模式条件
*/
evaluateOperatorCondition(rule, context) {
const { field, operator, value } = rule;
if (!field || !operator) {
return false;
}
const fieldValue = this.getFieldValue(field, context);
const handler = this.operatorHandlers.get(operator);
if (handler) {
return handler(fieldValue, value, rule);
}
return this.evaluateBuiltInOperator(fieldValue, operator, value);
}
/**
* 获取字段值
*/
getFieldValue(field, context) {
const path = field.split(".");
let value = context.attributes;
for (const key of path) {
if (value && typeof value === "object" && key in value) {
value = value[key];
} else {
return void 0;
}
}
return value;
}
/**
* 评估内置运算符
*/
evaluateBuiltInOperator(fieldValue, operator, compareValue) {
switch (operator) {
case "=":
case "==":
return fieldValue == compareValue;
case "!=":
return fieldValue != compareValue;
case ">":
return fieldValue > compareValue;
case ">=":
return fieldValue >= compareValue;
case "<":
return fieldValue < compareValue;
case "<=":
return fieldValue <= compareValue;
case "in":
return Array.isArray(compareValue) && compareValue.includes(fieldValue);
case "not_in":
return Array.isArray(compareValue) && !compareValue.includes(fieldValue);
case "contains":
if (Array.isArray(fieldValue)) {
return fieldValue.includes(compareValue);
}
if (typeof fieldValue === "string") {
return fieldValue.includes(compareValue);
}
return false;
case "not_contains":
if (Array.isArray(fieldValue)) {
return !fieldValue.includes(compareValue);
}
if (typeof fieldValue === "string") {
return !fieldValue.includes(compareValue);
}
return true;
case "starts_with":
return typeof fieldValue === "string" && fieldValue.startsWith(compareValue);
case "ends_with":
return typeof fieldValue === "string" && fieldValue.endsWith(compareValue);
case "regex":
return typeof fieldValue === "string" && new RegExp(compareValue).test(fieldValue);
case "between":
if (!Array.isArray(compareValue) || compareValue.length !== 2)
return false;
return fieldValue >= compareValue[0] && fieldValue <= compareValue[1];
case "is_null":
return fieldValue === null || fieldValue === void 0;
case "is_not_null":
return fieldValue !== null && fieldValue !== void 0;
case "is_empty":
if (Array.isArray(fieldValue))
return fieldValue.length === 0;
if (typeof fieldValue === "string")
return fieldValue.length === 0;
if (typeof fieldValue === "object")
return Object.keys(fieldValue).length === 0;
return !fieldValue;
case "is_not_empty":
if (Array.isArray(fieldValue))
return fieldValue.length > 0;
if (typeof fieldValue === "string")
return fieldValue.length > 0;
if (typeof fieldValue === "object")
return Object.keys(fieldValue).length > 0;
return Boolean(fieldValue);
case "product_match":
return this.evaluateProductMatch(fieldValue, compareValue);
case "object_in":
return this.evaluateProductMatch(fieldValue, compareValue);
default:
throw new Error(`Unsupported operator: ${operator}`);
}
}
/**
* 根据 actionIds 获取 ActionEffect 对象
*/
getActionsByIds(actionIds, actions) {
const matchedActions = [];
const actionMap = new Map(actions.map((action) => [action.id, action]));
for (const id of actionIds) {
const action = actionMap.get(id);
if (action) {
matchedActions.push(action);
} else {
if (this.options.debug) {
console.warn(`Action with id "${id}" not found in actions array`);
}
}
}
return matchedActions;
}
/**
* 按 priority 排序(降序)
*/
sortActionsByPriority(actions) {
return [...actions].sort((a, b) => {
const priorityA = a.priority ?? 0;
const priorityB = b.priority ?? 0;
return priorityB - priorityA;
});
}
/**
* 判断是否为条件组
*/
isConditionGroup(rule) {
return "operator" in rule && "rules" in rule && Array.isArray(rule.rules);
}
/**
* 验证配置
*/
validateConfig(config) {
if (!config) {
throw new Error("Strategy config is required");
}
if (!config.metadata || !config.metadata.id) {
throw new Error("Strategy metadata.id is required");
}
if (!config.conditions) {
throw new Error("Strategy conditions is required");
}
if (!Array.isArray(config.actions)) {
throw new Error("Strategy actions must be an array");
}
}
/**
* 初始化内置运算符处理器
*/
initializeBuiltInOperators() {
}
/**
* 评估商品匹配运算符
*
* 匹配规则:
* - 首先匹配 product_id
* - 如果配置的 product_variant_id = 0,只需要 product_id 匹配(表示任意变体)
* - 如果配置的 product_variant_id != 0,还需要 product_variant_id 精确匹配
*
* @param fieldValue 实际商品 { product_id: number, product_variant_id: number }
* @param configValue 配置的商品列表 Array<{ product_id: number, product_variant_id: number }>
* @returns 是否匹配
*/
evaluateProductMatch(fieldValue, configValue) {
if (!Array.isArray(configValue)) {
return false;
}
if (!fieldValue || typeof fieldValue !== "object" || typeof fieldValue.product_id !== "number") {
return false;
}
const actualProductId = fieldValue.product_id;
const actualVariantId = fieldValue.product_variant_id ?? 0;
return configValue.some((item) => {
if (!item || typeof item.product_id !== "number") {
return false;
}
const configProductId = item.product_id;
const configVariantId = item.product_variant_id ?? 0;
if (configProductId !== actualProductId) {
return false;
}
if (configVariantId === 0) {
return true;
}
return configVariantId === actualVariantId;
});
}
/**
* 注册自定义运算符
*/
registerOperator(operator, handler) {
this.operatorHandlers.set(operator, handler);
}
/**
* 获取调试信息
*/
getDebugInfo() {
return {
debug: this.options.debug,
enableTrace: this.options.enableTrace,
registeredOperators: Array.from(this.operatorHandlers.keys())
};
}
};
function createStrategyEngine(options) {
return new StrategyEngine(options);
}
function evaluate(config, context) {
const engine = new StrategyEngine();
return engine.evaluate(config, context);
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
StrategyEngine,
createStrategyEngine,
evaluate
});