当前位置: 首页 > article >正文

使用json配置动态区间及动态执行公式

背景

       有时候可能线上一直需要调整公式或者区间以及参数等等,如果使用代码方式,将会变得比较麻烦,可以在redis或者数据库配置一份动态配置,让代码进行解析并执行,可以对公式以及参数等进行动态调节

需求

x 是估值,n是区间 所属值
得分最大值:x-n=批发价得100分
得分最小值:批发价小于x-4n,大于x+n得0分

批发价在区间[x-4n,x-n]使用公式:100-(批发价-(x-n) )的绝对值/0.03n (注意区间的开闭)

批发价在区间(x-n,x+n]使用公式:100-(批发价-(x-n) )的绝对值/0.02n

批发价区间划分:(划分逻辑,中位数*0.2/4(满足偏差的最大值等于中位数的20%))
● 10000(批发价)以下 300 (n值)
● 10000-19999 750
● 20000-29999 1250
● 30000-49999 2000
● 50000-79999 3250
● 80000-99999 4500
● 100000-149999 6500
● 150000-199999 9000
● 200000-299999 15000
● 300000-499999 20000
● 500000以上 25000
逻辑1.2:批发价为空/批发等于零售价/批发价大于零售价=0分

实现

json配置

{
  "realWholesaleScoringSystem": {
    "description": "评分系统",
    "totalScore": 100,
    "coefficient": 0.00218727,
    "scoringCriteria": [
      {
        "condition": "批发价小于零售价,在指定区间,动态配置",
        "code": "wholesale_price_lt_retail_price_in_dynamic_range",
        "ranges": [
          {
            "min": "x - 4 * n",
            "max": "x - n",
            "inclusiveMin": "closed",
            "inclusiveMinDesc": "区间最小值开闭值",
            "inclusiveMax": "closed",
            "inclusiveMaxDesc": "区间最大值开闭值",
            "scoreFormula": "100 - Math.abs(wholesalePrice - (x - n)) / (0.03 * n)"
          },
          {
            "min": "x - n",
            "max": "x + n",
            "inclusiveMin": "open",
            "inclusiveMinDesc": "区间最小值开闭值",
            "inclusiveMax": "closed",
            "inclusiveMaxDesc": "区间最大值开闭值",
            "scoreFormula": "100 - Math.abs(wholesalePrice - (x - n)) / (0.02 * n)"
          }
        ],
        "notInRangeScore": 0,
        "formulaDescription": "批发价小于零售价在不同区间的得分计算"
      },      {
        "condition": "有批发价,等于零售价",
        "code": "wholesale_price_eq_retail_price",
        "score": 60
      },
      {
        "condition": "无批发价或批发价大于零售价",
        "code": "wholesale_price_is_null_or_gt_retail_price",
        "score": 0
      },
      {
        "condition": "有批发价,无零售价",
        "code": "wholesale_price_exists_no_retail_price",
        "score": 100
      }
    ],
    "priceRangeSettings-n_value": [
      {
        "priceRange": {
          "minPrice": 0,
          "maxPrice": 9999,
          "nValue": 300
        }
      },
      {
        "priceRange": {
          "minPrice": 10000,
          "maxPrice": 19999,
          "nValue": 750
        }
      },
      {
        "priceRange": {
          "minPrice": 20000,
          "maxPrice": 29999,
          "nValue": 1250
        }
      },
      {
        "priceRange": {
          "minPrice": 30000,
          "maxPrice": 49999,
          "nValue": 2000
        }
      },
      {
        "priceRange": {
          "minPrice": 50000,
          "maxPrice": 79999,
          "nValue": 3250
        }
      },
      {
        "priceRange": {
          "minPrice": 80000,
          "maxPrice": 99999,
          "nValue": 4500
        }
      },
      {
        "priceRange": {
          "minPrice": 100000,
          "maxPrice": 149999,
          "nValue": 6500
        }
      },
      {
        "priceRange": {
          "minPrice": 150000,
          "maxPrice": 199999,
          "nValue": 9000
        }
      },
      {
        "priceRange": {
          "minPrice": 200000,
          "maxPrice": 299999,
          "nValue": 15000
        }
      },
      {
        "priceRange": {
          "minPrice": 300000,
          "maxPrice": 499999,
          "nValue": 20000
        }
      },
      {
        "priceRange": {
          "minPrice": 500000,
          "maxPrice": null,
          "nValue": 25000
        }
      }
    ]
  }
}

代码实现

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;

public class WholesaleScoringSystem {


    // JavaScript 引擎,用于动态计算公式
    // 动态执行脚本
    // 使用 ThreadLocal 为每个线程提供独立的 ScriptEngine 实例
    private static final ThreadLocal<ScriptEngine> engine = ThreadLocal.withInitial(() ->
            new ScriptEngineManager().getEngineByName("JavaScript")
    );

    // 获取当前线程的 ScriptEngine 实例
    public static ScriptEngine getScriptEngine() {
        return engine.get();
    }

    // 移除ScriptEngine 实例
    public static void removeScriptEngine() {
        engine.remove();
    }

    public static void main(String[] args) {
        // 加载 JSON 配置(可从文件或其他来源获取)
        String jsonConfig = jsonStr;
        JSONObject wholesaleScoring = JSONObject.parseObject(jsonConfig).getJSONObject("realWholesaleScoringSystem");

        // 测试不同场景的评分计算
        testScenarios(wholesaleScoring);
    }

    /**
     * 测试不同批发价情况下的评分计算
     *
     * @param wholesaleScoring 真批发评分系统的配置信息
     */
    private static void testScenarios(JSONObject wholesaleScoring) {
        // 设置零售价和估价值 x
        Long retailPrice = 50000L;
        Long x = 50000L;

        // 预设不同的批发价用于测试
//        Long[] wholesalePrices = {46000L, 48000L, 52000L, 50000L, 55000L, null, 49000L};
        Long[] wholesalePrices = {1000L, 15000L, 25000L, 45000L, 120000L, 500000L, 46000L, 48000L, 52000L, 50000L, 55000L, null, 49000L};

        // 遍历每个批发价并计算评分
        for (Long wholesalePrice : wholesalePrices) {
            BigDecimal score = computeWholesaleScore(wholesaleScoring, wholesalePrice, retailPrice, x);
            System.out.println("批发价: " + wholesalePrice + ", 评分: " + score);
        }
    }

    /**
     * 计算批发评分
     *
     * @param wholesaleScoring 真批发评分系统的配置信息
     * @param wholesalePrice   批发价
     * @param retailPrice      零售价
     * @param x               估价值
     * @return 计算后的评分
     */
    private static BigDecimal computeWholesaleScore(JSONObject wholesaleScoring, Long wholesalePrice, Long retailPrice, Long x) {
        // 获取评分标准的配置信息
        JSONArray scoringCriteria = wholesaleScoring.getJSONArray("scoringCriteria");
        // 获取评分系数
        BigDecimal sourceCoefficient = wholesaleScoring.getBigDecimal("coefficient");

        // 遍历每个评分标准条件
        for (Object obj : scoringCriteria) {
            JSONObject criterion = (JSONObject) obj;
            String code = criterion.getString("code");

            switch (code) {
                case "wholesale_price_lt_retail_price_in_dynamic_range":
                    // 判断批发价是否存在且低于零售价
                    if (wholesalePrice != null && wholesalePrice < retailPrice) {
                        // 根据批发价获取对应的 n 值
                        Long nValue = getNValue(wholesaleScoring.getJSONArray("priceRangeSettings-n_value"), wholesalePrice);
                        if (nValue == null) continue; // 如果没有找到合适的 n 值,跳过该条件

                        // 获取范围设置
                        JSONArray ranges = criterion.getJSONArray("ranges");
                        boolean isInRange = false;

                        // 遍历所有范围,判断批发价是否在范围内
                        for (Object rangeObj : ranges) {
                            JSONObject range = (JSONObject) rangeObj;
                            // 动态计算范围的 min 和 max
                            BigDecimal min = evaluateExpression(range.getString("min"), new HashMap<String, Object>() {{
                                put("n", nValue);
                                put("x", x);
                            }});
                            BigDecimal max = evaluateExpression(range.getString("max"), new HashMap<String, Object>() {{
                                put("n", nValue);
                                put("x", x);
                            }});

                            // 解析区间的开闭属性
                            String inclusiveMin = range.getString("inclusiveMin");
                            String inclusiveMax = range.getString("inclusiveMax");

                            // 判断批发价是否在当前区间内
                            boolean withinMinBound = "closed".equals(inclusiveMin) ? BigDecimal.valueOf(wholesalePrice).compareTo(min) >= 0 : BigDecimal.valueOf(wholesalePrice).compareTo(min) > 0;
                            boolean withinMaxBound = "closed".equals(inclusiveMax) ? BigDecimal.valueOf(wholesalePrice).compareTo(max) <= 0 : BigDecimal.valueOf(wholesalePrice).compareTo(max) < 0;

                            if (withinMinBound && withinMaxBound) {
                                isInRange = true;
                                // 根据配置的公式计算得分
                                String formula = range.getString("scoreFormula");
                                BigDecimal score = evaluateExpression(formula, new HashMap<String, Object>() {{
                                    put("wholesalePrice", wholesalePrice);
                                    put("x", x);
                                    put("n", nValue);
                                }});
                                return score.multiply(sourceCoefficient); // 返回乘以系数后的得分
                            }
                        }

                        // 如果批发价不在任何指定的范围内,使用 notInRangeScore
                        if (!isInRange) {
                            return BigDecimal.valueOf(criterion.getDouble("notInRangeScore")).multiply(sourceCoefficient);
                        }
                    }
                    break;

                case "wholesale_price_eq_retail_price":
                    // 判断批发价是否等于零售价
                    if (wholesalePrice != null && retailPrice != null && wholesalePrice.equals(retailPrice)) {
                        return BigDecimal.valueOf(criterion.getDouble("score")).multiply(sourceCoefficient);
                    }
                    break;

                case "wholesale_price_is_null_or_gt_retail_price":
                    // 判断批发价是否为空或大于零售价
                    if (wholesalePrice == null || (wholesalePrice != null && retailPrice != null && wholesalePrice > retailPrice)) {
                        return BigDecimal.valueOf(criterion.getDouble("score")).multiply(sourceCoefficient);
                    }
                    break;

                case "wholesale_price_exists_no_retail_price":
                    // 判断是否有批发价但无零售价
                    if (wholesalePrice != null && retailPrice == null) {
                        return BigDecimal.valueOf(criterion.getDouble("score")).multiply(sourceCoefficient);
                    }
                    break;

                default:
                    break;
            }
        }

        // 如果没有匹配到任何条件,返回 0 分
        return BigDecimal.ZERO;
    }

    /**
     * 动态计算表达式的值
     *
     * @param expression 要计算的表达式
     * @param variables  表达式中使用的变量,以键值对的形式传入
     * @return 计算结果
     */
    private static BigDecimal evaluateExpression(String expression, Map<String, Object> variables) {
        try {

            ScriptEngine scriptEngine = getScriptEngine();
            // 将所有变量放入引擎上下文
            for (Map.Entry<String, Object> entry : variables.entrySet()) {
                scriptEngine.put(entry.getKey(), entry.getValue());
            }

            // 计算表达式并返回结果
            BigDecimal result = BigDecimal.valueOf(((Number) scriptEngine.eval(expression)).doubleValue());
            removeScriptEngine();

            return result;
        } catch (ScriptException e) {
            e.printStackTrace();
            return BigDecimal.ZERO;
        }
    }


    /**
     * 根据批发价在价格范围设置中获取对应的 n 值
     *
     * @param priceRangeSettings 价格范围设置
     * @param wholesalePrice 批发价
     * @return 对应的 n 值
     */
    private static Long getNValue(JSONArray priceRangeSettings, Long wholesalePrice) {
        // 遍历价格范围设置,以确定 n 值
        for (Object obj : priceRangeSettings) {
            JSONObject rangeSetting = (JSONObject) obj;
            JSONObject priceRange = rangeSetting.getJSONObject("priceRange");
            Long minPrice = priceRange.getLong("minPrice");
            Long maxPrice = priceRange.containsKey("maxPrice") ? priceRange.getLong("maxPrice") : Long.MAX_VALUE;

            // 如果批发价在范围内,则返回相应的 n 值
            if (wholesalePrice >= minPrice && wholesalePrice <= maxPrice) {
                return priceRange.getLong("nValue");
            }
        }
        return null; // 若没有找到匹配的范围,则返回 null
    }

    private static final String jsonStr = "{\n" +
            "  \"realWholesaleScoringSystem\": {\n" +
            "    \"description\": \"评分系统\",\n" +
            "    \"totalScore\": 100,\n" +
            "    \"coefficient\": 0.00218727,\n" +
            "    \"scoringCriteria\": [\n" +
            "      {\n" +
            "        \"condition\": \"批发价小于零售价,在指定区间,动态配置\",\n" +
            "        \"code\": \"wholesale_price_lt_retail_price_in_dynamic_range\",\n" +
            "        \"ranges\": [\n" +
            "          {\n" +
            "            \"min\": \"x - 4 * n\",\n" +
            "            \"max\": \"x - n\",\n" +
            "            \"inclusiveMin\": \"closed\",\n" +
            "            \"inclusiveMinDesc\": \"区间最小值开闭值\",\n" +
            "            \"inclusiveMax\": \"closed\",\n" +
            "            \"inclusiveMaxDesc\": \"区间最大值开闭值\",\n" +
            "            \"scoreFormula\": \"100 - Math.abs(wholesalePrice - (x - n)) / (0.03 * n)\"\n" +
            "          },\n" +
            "          {\n" +
            "            \"min\": \"x - n\",\n" +
            "            \"max\": \"x + n\",\n" +
            "            \"inclusiveMin\": \"open\",\n" +
            "            \"inclusiveMinDesc\": \"区间最小值开闭值\",\n" +
            "            \"inclusiveMax\": \"closed\",\n" +
            "            \"inclusiveMaxDesc\": \"区间最大值开闭值\",\n" +
            "            \"scoreFormula\": \"100 - Math.abs(wholesalePrice - (x - n)) / (0.02 * n)\"\n" +
            "          }\n" +
            "        ],\n" +
            "        \"notInRangeScore\": 0,\n" +
            "        \"formulaDescription\": \"批发价小于零售价在不同区间的得分计算\"\n" +
            "      },      {\n" +
            "        \"condition\": \"有批发价,等于零售价\",\n" +
            "        \"code\": \"wholesale_price_eq_retail_price\",\n" +
            "        \"score\": 60\n" +
            "      },\n" +
            "      {\n" +
            "        \"condition\": \"无批发价或批发价大于零售价\",\n" +
            "        \"code\": \"wholesale_price_is_null_or_gt_retail_price\",\n" +
            "        \"score\": 0\n" +
            "      },\n" +
            "      {\n" +
            "        \"condition\": \"有批发价,无零售价\",\n" +
            "        \"code\": \"wholesale_price_exists_no_retail_price\",\n" +
            "        \"score\": 100\n" +
            "      }\n" +
            "    ],\n" +
            "    \"priceRangeSettings-n_value\": [\n" +
            "      {\n" +
            "        \"priceRange\": {\n" +
            "          \"minPrice\": 0,\n" +
            "          \"maxPrice\": 9999,\n" +
            "          \"nValue\": 300\n" +
            "        }\n" +
            "      },\n" +
            "      {\n" +
            "        \"priceRange\": {\n" +
            "          \"minPrice\": 10000,\n" +
            "          \"maxPrice\": 19999,\n" +
            "          \"nValue\": 750\n" +
            "        }\n" +
            "      },\n" +
            "      {\n" +
            "        \"priceRange\": {\n" +
            "          \"minPrice\": 20000,\n" +
            "          \"maxPrice\": 29999,\n" +
            "          \"nValue\": 1250\n" +
            "        }\n" +
            "      },\n" +
            "      {\n" +
            "        \"priceRange\": {\n" +
            "          \"minPrice\": 30000,\n" +
            "          \"maxPrice\": 49999,\n" +
            "          \"nValue\": 2000\n" +
            "        }\n" +
            "      },\n" +
            "      {\n" +
            "        \"priceRange\": {\n" +
            "          \"minPrice\": 50000,\n" +
            "          \"maxPrice\": 79999,\n" +
            "          \"nValue\": 3250\n" +
            "        }\n" +
            "      },\n" +
            "      {\n" +
            "        \"priceRange\": {\n" +
            "          \"minPrice\": 80000,\n" +
            "          \"maxPrice\": 99999,\n" +
            "          \"nValue\": 4500\n" +
            "        }\n" +
            "      },\n" +
            "      {\n" +
            "        \"priceRange\": {\n" +
            "          \"minPrice\": 100000,\n" +
            "          \"maxPrice\": 149999,\n" +
            "          \"nValue\": 6500\n" +
            "        }\n" +
            "      },\n" +
            "      {\n" +
            "        \"priceRange\": {\n" +
            "          \"minPrice\": 150000,\n" +
            "          \"maxPrice\": 199999,\n" +
            "          \"nValue\": 9000\n" +
            "        }\n" +
            "      },\n" +
            "      {\n" +
            "        \"priceRange\": {\n" +
            "          \"minPrice\": 200000,\n" +
            "          \"maxPrice\": 299999,\n" +
            "          \"nValue\": 15000\n" +
            "        }\n" +
            "      },\n" +
            "      {\n" +
            "        \"priceRange\": {\n" +
            "          \"minPrice\": 300000,\n" +
            "          \"maxPrice\": 499999,\n" +
            "          \"nValue\": 20000\n" +
            "        }\n" +
            "      },\n" +
            "      {\n" +
            "        \"priceRange\": {\n" +
            "          \"minPrice\": 500000,\n" +
            "          \"maxPrice\": null,\n" +
            "          \"nValue\": 25000\n" +
            "        }\n" +
            "      }\n" +
            "    ]\n" +
            "  }\n" +
            "}\n";
}

运行效果

在这里插入图片描述


http://www.kler.cn/a/397201.html

相关文章:

  • LLMs之Code:Qwen2.5-Coder的简介、安装和使用方法、案例应用之详细攻略
  • oneplus3t-Lineage16.1-Android.bp
  • STM32 标准库函数 GPIO_SetBits、GPIO_ResetBits、GPIO_WriteBit、GPIO_Write 区别
  • Apache Paimon、Apache Hudi、Apache Iceberg对比分析
  • 探索SAP财务管理软件:重塑企业财务管理新境界
  • 在 Oracle Linux 8.9 上安装Oracle Database 23ai 23.5
  • IOPaint模型部署教程
  • 【图像压缩感知】论文阅读:Content-Aware Scalable Deep Compressed Sensing
  • Oracle EBS FORM界面获取某LOV的方法
  • CentOS 源码安装FFmpeg
  • 【Qt】报错error: undefined reference to `vtable for的最简单解决
  • 企业选择CPU服务器都有哪些用途?
  • 内部排序和外部排序以及常见算法和时间复杂度
  • C# VS的常用功能(一) 视图篇
  • 小地图(二)
  • yaml的学习记录
  • 我们是如何实现 TiDB Cloud Serverless 的 - 成本篇
  • Java爬虫(HttpURLConnection)详解
  • 分布式----Ceph部署
  • 动态规划不同维度分析leetcode198.打家劫舍问题
  • 如何选择国产化CMS来建设政务网站?
  • 创建vue+electron项目流程
  • Ubuntu终端跑colmap实验记录——生成sparse和poses_bounds.npy
  • FFmpeg 4.3 音视频-多路H265监控录放C++开发十四,总结编码过程,从摄像头获得数据后,转成AVFrame,然后再次转成AVPacket,
  • Linux设置静态IP
  • 鸿蒙进阶篇-定时器、递归