项目中有个需求:对客户画像进行打分,客户画像的一些标签有:年龄、家庭年收入、所在省份、想购买什么保险、家庭人口等。
这个客户的评分就是由这些画像标签的评分组合起来的,这个组合有一个公式,如下所示:
1/(1+exp(-(0.00188
+(0.49799*age)
+(0.47557*createputinintervald)
+(0.37217*mediatypename)
+(0.33848*isdact)
+(0.18899*forinsur)
+(0.3288*province)
+(0.28564*wantbuy)
+(0.17985*createtimetoappointmenttime)
+(0.15602*understandtherisks_counts)
+(0.10727*fertilitycircumstance)
+(0.19211*isbuycommercialinsurance_jc)
+(0.05253*pushtypename)
)))
现在问题是:每个客户的详细标签的内容不一样,那么他们的标签评分也不一样,最终的客户评分也不一样,这就需要我们动态的执行上面这个公式,在执行公式的时候,里面的变量值需要动态替换。我的实现思路如下,具体代码如下。
@Slf4j
public enum CustScoreFormulaEnum {
SCORE_FORMULA("1/(1+Math.exp(-(0.00188+(0.49799*"+FieldNameEnum.AGE.fieldName+")" +
"+(0.47557*"+FieldNameEnum.CREATE_PUT_IN_INTERVAL.fieldName+")" +
"+(0.37217*"+FieldNameEnum.MEDIA_TYPENAME.fieldName+")" +
"+(0.33848*"+FieldNameEnum.IS_DAC_T.fieldName+")" +
"+(0.18899*"+FieldNameEnum.FOR_INSURE.fieldName+")" +
"+(0.3288*"+FieldNameEnum.PROVINCE.fieldName+")" +
"+(0.28564*"+FieldNameEnum.WANT_BUY.fieldName+")" +
"+(0.17985*"+FieldNameEnum.CREATE_TIME_TO_APPOINTMENT_TIME.fieldName+")" +
"+(0.15602*"+FieldNameEnum.UNDERSTAND_THE_RISKS_COUNTS.fieldName+")" +
"+(0.10727*"+FieldNameEnum.FERTILITY_CIRCUMSTANCE.fieldName+")" +
"+(0.19211*"+FieldNameEnum.IS_BUY_COMMERCIAL_INSURANCE.fieldName+")" +
"+(0.05253*"+FieldNameEnum.PUSH_TYPE_NAME.fieldName+"))))","客户评分公式");
/**
* 公式
*/
public final String formula;
/**
* 公式具体描述
*/
public final String formulaDesc;
CustScoreFormulaEnum(String formula, String formulaDesc) {
this.formula = formula;
this.formulaDesc = formulaDesc;
}
/**
* @description 动态执行公式
* @param fieldMapping 字段名称和字段值的mapping
* @return java.lang.Double
**/
public static Double calc(Map<String,Double> fieldMapping) {
String formula = CustScoreFormulaEnum.SCORE_FORMULA.formula;
//先把公式里面的字段key值动态替换成数字
for (String fieldName : fieldMapping.keySet()) {
formula = formula.replace(fieldName, fieldMapping.get(fieldName).toString());
}
//打印替换完后的公式
log.info("formula公式为:{}", formula);
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("JavaScript");
try {
// 执行字符串公式
Object result = engine.eval(formula);
//将结果保留4位小数
return Math.round(Double.parseDouble(result.toString()) * 10000D) / 10000D;
} catch (Exception e) {
log.error("ScriptEngine 执行 字符串公式异常", e);
}
return 0D;
}
}
其中FieldNameEnum 这个枚举存的是每个字段英文名称和中文描述,还有每个字段的评分值我也会记录在表里面,相当于一个中间值的记录,这里具体的代码我也不做展示。