最近我的任务是为后端的产品创建一个计算机制。有许多产品在计算方面是独一无二的。由于将有一个用于添加新产品的管理面板,我希望使其尽可能动态,这样我就不必编辑后端来添加简单的公式。在数据库中,在 product 表中,我想指定一列作为 JSON 类型来存储计算的蓝图:
calculation: {
bottom: {
length: "%v + 100",
width: "%v + 100",
UC: "%v * 12",
},
side: {
length: "%v - 50",
width: "%v + 4",
UC: "%v * 11",
},
},
我想不出更好的主意来为我的传入变量插入占位符
%v
。
结构将是相同的。意思是,唯一的动态部分是属性的能力(在示例中是bottom和side)。
现在的问题是,有了这个想法,评估蓝图确实很危险 因为我必须使用
.eval()
。例如,我创建了以下代码:
// Example
const products = [
{
id: 1,
name: "something",
calculation: {
bottom: {
length: "%v + 100",
width: "%v + 100",
UC: "%v * 12",
},
side: {
length: "%v - 50",
width: "%v + 4",
UC: "%v * 11",
},
},
},
];
// calculation function
function handleCalculation(calcObj, initialValues) {
const updatedCalcObj = {};
for (let prop in calcObj) {
updatedCalcObj[prop] = {};
for (let attr in calcObj[prop]) {
const expression = calcObj[prop][attr].replace(
"%v",
initialValues[prop][attr]
);
// !!! DANGER IS HERE.
updatedCalcObj[prop][attr] = eval(expression);
}
}
return updatedCalcObj;
}
// example of body of a request
const initialValues = {
bottom: {
length: 100,
width: 200,
UC: 1,
},
side: {
length: 120,
width: 50,
UC: 1,
},
};
// seeing the result
console.log(handleCalculation(products[0].calculation, initialValues))
您遇到过类似的情况吗?有更好的方法来解决这个问题吗?
利用 JSON 和数学表达式都可以解析为树的事实并定义一个简单的解析器:
// Example
const products = [
{
id: 1,
name: "something",
calculation: {
bottom: {
length: {add: ["%v", 100]},
width: {add: ["%v", 100]},
UC: {mult: ["%v", 12]},
},
side: {
length: {sub: ["%v", 50]},
width: {add: ["%v", 4]},
UC: {mult: ["%v", 11]},
},
nested_example: {
// (v + 100) * (v / 2 - 20)
result: {mult: [
{add: ["%v", 100]},
{sub: [
{div:["%v", 2]},
20
]},
]},
},
},
},
];
const operations = {
add: (a, b) => a + b,
sub: (a, b) => a - b,
mult: (a, b) => a * b,
div: (a, b) => a / b,
};
function calculate(calcObj, initialValue) {
if (calcObj === "%v") return initialValue;
let [operation, [left, right]] = Object.entries(calcObj)[0];
if (typeof left !== "number") {
left = calculate(left, initialValue);
}
if (typeof right !== "number") {
right = calculate(right, initialValue);
}
return operations[operation](left, right);
}
// calculation function
function handleCalculation(calcObj, initialValues) {
const updatedCalcObj = {};
for (let prop in calcObj) {
updatedCalcObj[prop] = {};
for (let attr in calcObj[prop]) {
const calculation = calcObj[prop][attr];
const value = initialValues[prop][attr];
updatedCalcObj[prop][attr] = calculate(calculation, value);
}
}
return updatedCalcObj;
}
// example of body of a request
const initialValues = {
bottom: {
length: 100,
width: 200,
UC: 1,
},
side: {
length: 120,
width: 50,
UC: 1,
},
nested_example: {
result: 100,
},
};
// seeing the result
console.log(handleCalculation(products[0].calculation, initialValues))