使用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) {// 设置零售价和估价值 xLong 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 和 maxBigDecimal 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); // 返回乘以系数后的得分}}// 如果批发价不在任何指定的范围内,使用 notInRangeScoreif (!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.mzph.cn/web/59619.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

腾讯地图GL JS点标识监听:无dragend事件的经纬度获取方案

引入腾讯地图SDK <!--腾讯地图 API--><script charset"utf-8" src"https://map.qq.com/api/gljs?librariestools&v1.exp&key***"></script>构建地图容器 <div class"layui-card"><div class"layui-car…

Tiktok对接和内容发布申请流程

这段时间在搞AI生成视频&#xff0c;希望用户能一键发布到Tiktok&#xff0c;因此研究了一下Tiktok的开发者申请流程&#xff0c;发现好复杂&#xff0c;同时也发现Tiktok的开发也跟我一样&#xff0c;挺草台班子的 0、流程简述 废话不多说&#xff0c;Tiktok的开发者申请和…

企业生产环境-麒麟V10(ARM架构)操作系统部署kafka高可用集群

前言&#xff1a;Apache Kafka是一个分布式流处理平台&#xff0c;由LinkedIn开发并捐赠给Apache软件基金会。它主要用于构建实时数据流管道和流应用。Kafka具有高吞吐量、可扩展性和容错性的特点&#xff0c;适用于处理大量数据。 以下是Kafka的一些核心概念和特性&#xff1…

xcode-select: error: tool ‘xcodebuild‘ requires Xcode, but active developer

打开 .sh 文件所在的终端窗口&#xff0c;执行终端命令&#xff1a;sh 文件名.sh&#xff0c;出现如下错误&#xff1a; 解决办法&#xff1a;

CC6学习记录

&#x1f338; cc6 cc6和cc1的国外链其实后半条链子是一样的&#xff0c;但是cc6的不局限于jdk的版本和commons-collections的版本。 回忆一下cc1的后半条链子&#xff1a; LazyMap.get()->InvokerTransformer.transform() 这里我们就结合了URLDNS链的思路&#xff0c;在…

MTU-内核态(数据链路层或网络接口上能够传输的最大数据包大小)

MTU&#xff08;最大传输单元&#xff0c;Maximum Transmission Unit&#xff09;是网络中用于表示数据链路层或网络接口上能够传输的最大数据包大小。 1. 工作原理 MTU 决定了一个数据包&#xff08;包括头部和数据部分&#xff09;的最大长度。它影响到数据的传输&#xff…

【系统架构设计师】真题论文: 论软件可靠性评价(包括解题思路和素材)

更多内容请见: 备考系统架构设计师-专栏介绍和目录 文章目录 真题题目(2010年 试题4)解题思路论文素材参考软件可靠性评价的重要意义软件可靠性评价方法(1)基于模型的评价方法(2)基于测试的评价方法(3)基于现场数据的评价方法软件可靠性评价应用案例真题题目(2010年 …

嵌入式硬件实战基础篇(一)-STM32+DAC0832 可调信号发生器-产生方波-三角波-正弦波

引言&#xff1a;本内容主要用作于学习巩固嵌入式硬件内容知识&#xff0c;用于想提升下述能力&#xff0c;针对学习STM32与DAC0832产生波形以及波形转换&#xff0c;对于硬件的降压和对于前面硬件篇的实际运用&#xff0c;针对仿真的使用&#xff0c;具体如下&#xff1a; 设…

网络安全练习之 ctfshow_web

文章目录 VIP题目限免&#xff08;即&#xff1a;信息泄露题&#xff09;源码泄露前台JS绕过协议头信息泄露robots后台泄露phps源码泄露源码压缩包泄露版本控制泄露源码(git)版本控制泄露源码2(svn)vim临时文件泄露cookie泄露域名txt记录泄露敏感信息公布内部技术文档泄露编辑器…

ReactPress与WordPress:一场内容管理系统的较量

ReactPress Github项目地址&#xff1a;https://github.com/fecommunity/reactpress WordPress官网&#xff1a;https://wordpress.org/ ReactPress与WordPress&#xff1a;一场内容管理系统的较量 在当今数字化时代&#xff0c;内容管理系统&#xff08;CMS&#xff09;已成为…

IDEA调整警告级别【IntelliJ IDEA 2024.2.0.1】

文章目录 目前现状鼠标悬停&#xff0c;选择配置筛选 > 取消选择OK效果 目前现状 需要把提示改成只要显示error的5个 鼠标悬停&#xff0c;选择配置 筛选 > 取消选择 OK 效果

实现 MVC 模式

实现 MVC 模式,通常可以通过分离 Model、View 和 Controller 的职责来构建一个模块化、易于维护的应用程序。以下是 MVC 的实现步骤和代码示例,以 Java Spring Boot 为例,这样的实现可以方便地应用于 Web 应用程序: 1. Model 层:数据和业务逻辑 Model 层负责应用程序的核…

Vue3 + Vite 构建组件库的整体流程

Vue3 Vite 构建组件库的流程 本文教你如何用 Vue Vite&#xff0c;一步一步构建一个组件库并发布到 npm 的整体流程 1. 通过 vite 命令创建一个基本的项目结构&#xff08;这里选用 vue ts 的项目&#xff09; npm create vitelatest2. 在项目中创建一个 lib 目录&#xf…

游戏引擎学习第10天

视频参考:https://www.bilibili.com/video/BV1LyU3YpEam/ 介绍intel architecture reference manual 地址:https://www.intel.com/content/www/us/en/developer/articles/technical/intel-sdm.html RDTS&#xff08;读取时间戳计数器&#xff09;指令是 x86/x86_64 架构中的…

正则表达式(补充)

定义一个正则表达式 const 变量名 /表达式/ const reg /前端/ 匹配看字符串中有无前端俩字 正则对象上的一些方法 test() 用于查看正则表达式与指定的字符串是否匹配 const reg /前端/ const res reg.test(学前端&#xff0c;找黑马) //匹配到返回true,匹配不到返回fa…

linux怎么查看网络带宽命令

说明: 1、研发反馈访问中间件有时候很慢,查看zabbix监控,发现带宽很高 2、但不知道具体什么应用业务访问导致,所以要用查看带宽命令获取实时数据 要查看带宽使用情况,具体取决于您的操作系统和可用的工具,以下是一些常用的命令: Windows: 使用 netstat -e 命令查看网络接…

视频编码基础入门

文章目录 前言一、视频编码的目标二、 视频编码基本流程1. 采样与颜色空间转换2. 变换编码&#xff08;例如DCT&#xff09;3. 量化4. 熵编码5. 运动补偿和帧间预测6. 编码输出 三、视频编码的关键技术1. 帧类型2. GOP&#xff08;Group of Pictures&#xff09;结构3. 比特率控…

CSS 语法规范

基本语法结构 CSS 的基本语法结构包含 选择器 和 声明块,两者共同组成 规则集。规则集可以为 HTML 元素设置样式,使页面结构和样式实现分离,便于网页的美化和布局调整。 CSS 规则集的结构如下: selector {property: value; }选择器(Selector) 选择器用于指定需要应用…

Rust 语言学习笔记(五)

终于来到了 Rust 的精髓所在了&#xff0c;那就是使之不依赖于垃圾回收又能保障内存安全且高效运行的所有权系统(Ownership System)。想要用 Rust 做一个稍显规模项目必定绕不过它&#xff0c;所有权系统包括所有权(Ownership), 借用(Borrowing), 生命周期(Lifetimes)。 以下概…

【在Linux世界中追寻伟大的One Piece】多路转接epoll(续)

目录 1 -> epoll的工作方式 1.1 -> 水平触发(Level Triggered)工作模式 1.2 -> 边缘触发(Edge Triggered)工作模式 2 -> 对比LT与ET 3 -> 理解ET模式和非阻塞文件描述符 4 -> epoll的使用场景 5 -> epoll示例 5.1 -> epoll服务器(LT模式) 5.2…