【RuleUtil】适用于全业务场景的规则匹配快速开发工具
一、RuleUtil 开发背景
1.1 越来越多,越来越复杂的业务规则
1、规则的应用场景多
2、规则配置的参数类型多(ID、数值、文本、日期等等)
3、规则的参数条件多(大于、小于、等于、包含、不包含、区间等等)
4、规则的结构复杂(父规则、子规则、子场景、与或、优先级、命中数量限制)
1.2 目前规则匹配实现方式,以及对应痛点
现有实现方式使用了许多 if-else 对匹配参数和条件运算符进行判断,结构复杂,可读性较差
痛点一:规则条件组合方式多样,普通实现代码结构容易混乱;
痛点二:同一规则在多个应用场景的复用性较差;
痛点三:每次需求迭代都要改动核心匹配逻辑,效率低,测试成本高。
二、RuleUtil 如何解决痛点
2.1 核心思想
1、通过设计 Rule 结构和四个字段类型,对业务规则进行高度抽象和统一,提高代码可复用性
2、研发仅需实现从业务规则到 Rule 的转换方法,以及定义匹配参数结构即可,无需再编写复杂的匹配逻辑,交给 match 方法通通搞定
RuleUtil 核心规则类(Rule)
/*** 核心规则类*/
public class RuleV2 {// 主规则idprivate String id;// 子规则idprivate String subId;// 规则参数private Object param;// 规则字段private List<Field> fields;public static class Field {// 字段名private String name;// 字段键private String key;// 字段类型private RuleFieldTypeEnum type;// 配置条件private RuleFieldConditionEnum condition;// 字段值private Object value;}
}
RuleUtil 核心匹配方法(match)
/*** 规则工具类V2*/
@Slf4j
public class RuleUtilV2 {/*** 规则匹配** @param param 待匹配参数* @param rules 待匹配规则池* @return 命中规则池*/public static List<RuleV2> match(Object param, List<RuleV2> rules) {return match(param, rules, rules.size());}/*** 规则匹配** @param param 待匹配参数* @param rules 待匹配规则池* @param matchLimit 命中规则数量限制* @return 命中规则池*/public static List<RuleV2> match(Object param, List<RuleV2> rules, Integer matchLimit) {List<RuleV2> matchRules = new ArrayList<>();if (Objects.isNull(param) || CollUtil.isEmpty(rules) || Objects.isNull(matchLimit) || matchLimit <= NumberConst.ZERO) {return matchRules;}for (RuleV2 rule : rules) {if (CollUtil.isEmpty(rule.getFields())) {continue;}boolean isMatch = true;for (RuleV2.Field field : rule.getFields()) {if (StrUtil.isBlank(field.getName()) || Objects.isNull(field.getType()) || Objects.isNull(field.getCondition()) || Objects.isNull(field.getValue())) {isMatch = false;log.info("RuleUtil::match 规则匹配存在空字段 规则id:{} 规则字段:{}", rule.getId(), field);break;}// 根据规则字段名获取参数字段值Object paramFieldValueObj = ReflectUtil.getFieldValue(param, field.getName());// 获取规则字段值Object ruleFieldValueObj = field.getValue();if (RuleFieldTypeEnum.NORMAL.equals(field.getType())) {// 普通属性Set<String> paramFieldValues = collConvertToSet(paramFieldValueObj);Set<String> ruleFieldValues = collConvertToSet(ruleFieldValueObj);if (Set.of(RuleFieldConditionEnum.LT, RuleFieldConditionEnum.LE, RuleFieldConditionEnum.GT, RuleFieldConditionEnum.GE).contains(field.getCondition())) {isMatch = false;break;}if (RuleFieldConditionEnum.EQ.equals(field.getCondition()) && !Objects.equals(paramFieldValues, ruleFieldValues)) {// 等于isMatch = false;break;}if (RuleFieldConditionEnum.NE.equals(field.getCondition()) && Objects.equals(paramFieldValues, ruleFieldValues)) {// 不等于isMatch = false;break;}if (RuleFieldConditionEnum.IN.equals(field.getCondition()) && !CollUtil.containsAny(paramFieldValues, ruleFieldValues)) {// 包含isMatch = false;break;}if (RuleFieldConditionEnum.NIN.equals(field.getCondition()) && CollUtil.containsAny(paramFieldValues, ruleFieldValues)) {// 不包含isMatch = false;break;}}if (RuleFieldTypeEnum.NUMBER.equals(field.getType())) {// 数值属性if (Set.of(RuleFieldConditionEnum.IN, RuleFieldConditionEnum.NIN).contains(field.getCondition())) {Set<String> paramFieldValues = collConvertToSet(paramFieldValueObj);Set<String> ruleFieldValues = collConvertToSet(ruleFieldValueObj);if (RuleFieldConditionEnum.IN.equals(field.getCondition()) && !CollUtil.containsAny(paramFieldValues, ruleFieldValues)) {// 包含isMatch = false;break;}if (RuleFieldConditionEnum.NIN.equals(field.getCondition()) && CollUtil.containsAny(paramFieldValues, ruleFieldValues)) {// 不包含isMatch = false;break;}continue;}String paramFieldValue = String.valueOf(paramFieldValueObj);if (!NumberUtil.isNumber(paramFieldValue) || !NumberUtil.isNumber(String.valueOf(ruleFieldValueObj))) {isMatch = false;log.info("RuleUtil::match 规则匹配存在非数值字段 规则id:{} 规则字段值:{} 参数字段值:{}", rule.getId(), field.getValue(), paramFieldValue);break;}double paramFieldDouble = NumberUtil.parseDouble(paramFieldValue);double ruleFieldDouble = NumberUtil.parseDouble(String.valueOf(ruleFieldValueObj));// 数值属性if (RuleFieldConditionEnum.EQ.equals(field.getCondition()) && paramFieldDouble != ruleFieldDouble) {// 等于isMatch = false;break;}if (RuleFieldConditionEnum.NE.equals(field.getCondition()) && paramFieldDouble == ruleFieldDouble) {// 不等于isMatch = false;break;}if (RuleFieldConditionEnum.LT.equals(field.getCondition()) && paramFieldDouble >= ruleFieldDouble) {// 小于isMatch = false;break;}if (RuleFieldConditionEnum.LE.equals(field.getCondition()) && paramFieldDouble > ruleFieldDouble) {// 小于等于isMatch = false;break;}if (RuleFieldConditionEnum.GT.equals(field.getCondition()) && paramFieldDouble <= ruleFieldDouble) {// 大于isMatch = false;break;}if (RuleFieldConditionEnum.GE.equals(field.getCondition()) && paramFieldDouble < ruleFieldDouble) {// 大于等于isMatch = false;break;}}if (RuleFieldTypeEnum.NORMAL_KV.equals(field.getType()) && StrUtil.isNotBlank(field.getKey())) {// 普通键值对属性if (Set.of(RuleFieldConditionEnum.LT, RuleFieldConditionEnum.LE, RuleFieldConditionEnum.GT, RuleFieldConditionEnum.GE).contains(field.getCondition())) {isMatch = false;break;}Set<String> paramFieldValues = mapConvertToSet(paramFieldValueObj, field.getKey());Set<String> ruleFieldValues = collConvertToSet(ruleFieldValueObj);if (RuleFieldConditionEnum.EQ.equals(field.getCondition()) && !Objects.equals(paramFieldValues, ruleFieldValues)) {// 等于isMatch = false;break;}if (RuleFieldConditionEnum.NE.equals(field.getCondition()) && Objects.equals(paramFieldValues, ruleFieldValues)) {// 不等于isMatch = false;break;}if (RuleFieldConditionEnum.IN.equals(field.getCondition()) && !CollUtil.containsAny(paramFieldValues, ruleFieldValues)) {// 包含isMatch = false;break;}if (RuleFieldConditionEnum.NIN.equals(field.getCondition()) && CollUtil.containsAny(paramFieldValues, ruleFieldValues)) {// 不包含isMatch = false;break;}}if (RuleFieldTypeEnum.NUMBER_KV.equals(field.getType()) && StrUtil.isNotBlank(field.getKey())) {// 数值键值对属性if (Set.of(RuleFieldConditionEnum.IN, RuleFieldConditionEnum.NIN).contains(field.getCondition())) {Set<Double> paramFieldDoubles = mapConvertToSet(paramFieldValueObj, field.getKey()).stream().filter(NumberUtil::isNumber).map(NumberUtil::parseDouble).collect(Collectors.toSet());Set<Double> ruleFieldDoubles = collConvertToSet(ruleFieldValueObj).stream().filter(NumberUtil::isNumber).map(NumberUtil::parseDouble).collect(Collectors.toSet());if (RuleFieldConditionEnum.IN.equals(field.getCondition()) && !CollUtil.containsAny(paramFieldDoubles, ruleFieldDoubles)) {// 包含isMatch = false;break;}if (RuleFieldConditionEnum.NIN.equals(field.getCondition()) && CollUtil.containsAny(paramFieldDoubles, ruleFieldDoubles)) {// 不包含isMatch = false;break;}continue;}String paramFieldValue = IterUtil.getFirst(mapConvertToSet(paramFieldValueObj, field.getKey()));if (!NumberUtil.isNumber(paramFieldValue) || !NumberUtil.isNumber(String.valueOf(ruleFieldValueObj))) {isMatch = false;log.info("RuleUtil::match 规则匹配存在非数值字段 规则id:{} 规则字段值:{} 参数字段值:{}", rule.getId(), field.getValue(), paramFieldValue);break;}double paramFieldDouble = NumberUtil.parseDouble(paramFieldValue);double ruleFieldDouble = NumberUtil.parseDouble(String.valueOf(ruleFieldValueObj));// 数值属性if (RuleFieldConditionEnum.EQ.equals(field.getCondition()) && paramFieldDouble != ruleFieldDouble) {// 等于isMatch = false;break;}if (RuleFieldConditionEnum.NE.equals(field.getCondition()) && paramFieldDouble == ruleFieldDouble) {// 不等于isMatch = false;break;}if (RuleFieldConditionEnum.LT.equals(field.getCondition()) && paramFieldDouble >= ruleFieldDouble) {// 小于isMatch = false;break;}if (RuleFieldConditionEnum.LE.equals(field.getCondition()) && paramFieldDouble > ruleFieldDouble) {// 小于等于isMatch = false;break;}if (RuleFieldConditionEnum.GT.equals(field.getCondition()) && paramFieldDouble <= ruleFieldDouble) {// 大于isMatch = false;break;}if (RuleFieldConditionEnum.GE.equals(field.getCondition()) && paramFieldDouble < ruleFieldDouble) {// 大于等于isMatch = false;break;}}}if (isMatch) {matchRules.add(rule);if (matchRules.size() >= matchLimit) {return matchRules;}}}return matchRules;}/*** 把集合类型对象转换为set** @param obj 对象* @return set*/private static Set<String> collConvertToSet(Object obj) {Set<String> set = new HashSet<>();if (Objects.isNull(obj)) {return set;}if (obj instanceof Collection<?> paramFieldColl) {for (Object subObj : paramFieldColl) {set.add(String.valueOf(subObj));}} else {set.add(String.valueOf(obj));}return set;}/*** 把map类型对象转换为set** @param obj 对象* @param key 键* @return set*/private static Set<String> mapConvertToSet(Object obj, String key) {Set<String> set = new HashSet<>();if (Objects.isNull(obj) || StrUtil.isBlank(key) || !(obj instanceof Map<?, ?> map)) {return set;}for (Map.Entry<?, ?> entry : map.entrySet()) {if (key.equals(String.valueOf(entry.getKey()))) {if (entry.getValue() instanceof Collection<?> paramFieldColl) {for (Object subObj : paramFieldColl) {set.add(String.valueOf(subObj));}} else {set.add(String.valueOf(entry.getValue()));}break;}}return set;}
}
RuleUtil 把业务规则配置的字段类型,归纳为以下四种(代码枚举:RuleFieldTypeEnum),各个类型支持的场景如下:
/*** 规则项字段类型枚举*/
@Getter
@AllArgsConstructor
public enum RuleFieldTypeEnum {UNKNOWN(0, ""),NORMAL(1, "普通"),NUMBER(2, "数值"),NORMAL_KV(3, "普通键值对"),NUMBER_KV(4, "数值键值对"),;private final Integer code;private final String desc;
}
2.2 应用场景
应用场景一:列举以下四个业务规则示例,分别对应上面四种字段类型:
第一步:实现 convert 方法,将业务规则转换为 Rule 实体列表
根据示例转换后的 Rule 实体 json 结构如下:
第二步:根据【字段名-Field.name】【字段类型-Field.type】自行定义匹配 Rule 所需要的参数实体类。根据示例定义的 Param 实体结构如下:
/*** 规则匹配参数*/
class Param {// 品类组合id(普通)private List<Long> combIds;// 库存数量(数值)private Long stock;// 属性id-属性值id(普通键值对)private Map<Long, List<Long>> attrIdToAttrValIdMap;// 成分属性值id-属性数值(数值键值对)private Map<Long, Double> componentAttrValIdToValMap;
}
第三步:调用 RuleUtile.match 方法,得到 Param 命中的 Rule 列表。
根据示例 Param 创建一个待匹配对象
{"combIds": ["1-男装","3-上装"],"stock": 15,"attrIdToAttrValIdMap": {"10-适合类型": ["11-宽松","13-修身"],"20-织造方式": ["21-牛仔"]},"componentAttrValIdToValMap": {"10-棉": 15.0,"20-尼龙": 85.0}
}
调用 RuleUtile.match 方法如下,方法返回了能够命中的 Rule
match 方法使用反射,根据【Rule.name】获取 Param 对应字段的值进行条件匹配
应用场景二:假设业务规则发生变化,迭代为嵌套【且】【或】关系的业务规则
第一步:更新 convert 方法,将新的业务规则转换为 Rule 实体列表
根据示例转换后的 Rule 实体 json 结构如下:
⚠️注意:多个【Rule】之间是【或】关系,多个【Rule.Field】之间是【且】关系
第二步:定义的 Param 实体结构不变
第三步:调用 RuleUtile.match 方法如下,方法返回了能够命中的 Rule
场景一、场景二的完整单元测试用例代码:
public class RuleUtilTest {@Testpublic void demo01() {// 规则匹配参数TestParam param = new TestParam();param.setCombIds(List.of(1L, 3L));param.setStock(15L);param.setAttrIdToAttrValIdMap(Map.of(10L, List.of(11L, 13L), 20L, List.of(21L)));param.setComponentAttrValIdToValMap(Map.of(10L, 15.0, 20L, 85.0));// 品类组合RuleV2.Field fieldCombIds = new RuleV2.Field();fieldCombIds.setName(LambdaUtil.getFieldName(TestParam::getCombIds));fieldCombIds.setType(RuleFieldTypeEnum.NORMAL); // 普通fieldCombIds.setCondition(RuleFieldConditionEnum.IN); // 包含fieldCombIds.setValue(List.of(1L, 2L));RuleV2 rule01 = new RuleV2();rule01.setId("rule01");rule01.setFields(List.of(fieldCombIds));// 库存数量RuleV2.Field fieldStockGt = new RuleV2.Field();fieldStockGt.setName(LambdaUtil.getFieldName(TestParam::getStock));fieldStockGt.setType(RuleFieldTypeEnum.NUMBER); // 数值fieldStockGt.setCondition(RuleFieldConditionEnum.GT); // 大于fieldStockGt.setValue("10");RuleV2.Field fieldStockLt = new RuleV2.Field();fieldStockLt.setName(LambdaUtil.getFieldName(TestParam::getStock));fieldStockLt.setType(RuleFieldTypeEnum.NUMBER);fieldStockLt.setCondition(RuleFieldConditionEnum.LT); // 小于fieldStockLt.setValue("20");RuleV2 rule02 = new RuleV2();rule02.setId("rule02");rule02.setFields(List.of(fieldStockGt, fieldStockLt));// 属性id-属性值id(普通键值对)RuleV2.Field fieldAttrIdToAttrValIdMap = new RuleV2.Field();fieldAttrIdToAttrValIdMap.setName(LambdaUtil.getFieldName(TestParam::getAttrIdToAttrValIdMap));fieldAttrIdToAttrValIdMap.setKey("10");fieldAttrIdToAttrValIdMap.setType(RuleFieldTypeEnum.NORMAL_KV);fieldAttrIdToAttrValIdMap.setCondition(RuleFieldConditionEnum.IN); // 包含fieldAttrIdToAttrValIdMap.setValue(List.of(11L, 12L));RuleV2 rule03 = new RuleV2();rule03.setId("rule03");rule03.setFields(List.of(fieldAttrIdToAttrValIdMap));// 成分属性值id-属性数值RuleV2.Field fieldComponentAttrValIdToValMapGt = new RuleV2.Field();fieldComponentAttrValIdToValMapGt.setName(LambdaUtil.getFieldName(TestParam::getComponentAttrValIdToValMap));fieldComponentAttrValIdToValMapGt.setKey("10");fieldComponentAttrValIdToValMapGt.setType(RuleFieldTypeEnum.NUMBER_KV); // 数值键值对fieldComponentAttrValIdToValMapGt.setCondition(RuleFieldConditionEnum.GT); // 大于fieldComponentAttrValIdToValMapGt.setValue("10");RuleV2.Field fieldComponentAttrValIdToValMapLt = new RuleV2.Field();fieldComponentAttrValIdToValMapLt.setName(LambdaUtil.getFieldName(TestParam::getComponentAttrValIdToValMap));fieldComponentAttrValIdToValMapLt.setKey("10");fieldComponentAttrValIdToValMapLt.setType(RuleFieldTypeEnum.NUMBER_KV);fieldComponentAttrValIdToValMapLt.setCondition(RuleFieldConditionEnum.LT); // 小于fieldComponentAttrValIdToValMapLt.setValue("20");RuleV2 rule04 = new RuleV2();rule04.setId("rule04");rule04.setFields(List.of(fieldComponentAttrValIdToValMapGt, fieldComponentAttrValIdToValMapLt));// 调用 RuleUtil 规则匹配方法List<RuleV2> matchResults = RuleUtilV2.match(param, List.of(rule01, rule02, rule03, rule04));// 命中四种字段类型的规则assertEquals(List.of(rule01, rule02, rule03, rule04), matchResults);}@Testpublic void demo02() {// 规则匹配参数TestParam param = new TestParam();param.setCombIds(List.of(1L, 3L));param.setStock(15L);param.setAttrIdToAttrValIdMap(Map.of(10L, List.of(11L, 13L), 20L, List.of(21L)));param.setComponentAttrValIdToValMap(Map.of(10L, 15.0, 20L, 85.0));// 品类组合RuleV2.Field fieldCombIds = new RuleV2.Field();fieldCombIds.setName(LambdaUtil.getFieldName(TestParam::getCombIds));fieldCombIds.setType(RuleFieldTypeEnum.NORMAL); // 普通fieldCombIds.setCondition(RuleFieldConditionEnum.IN); // 包含fieldCombIds.setValue(List.of(1L, 2L));// 库存数量RuleV2.Field fieldStockGt = new RuleV2.Field();fieldStockGt.setName(LambdaUtil.getFieldName(TestParam::getStock));fieldStockGt.setType(RuleFieldTypeEnum.NUMBER); // 数值fieldStockGt.setCondition(RuleFieldConditionEnum.GT); // 大于fieldStockGt.setValue("10");RuleV2.Field fieldStockLt = new RuleV2.Field();fieldStockLt.setName(LambdaUtil.getFieldName(TestParam::getStock));fieldStockLt.setType(RuleFieldTypeEnum.NUMBER);fieldStockLt.setCondition(RuleFieldConditionEnum.LT); // 小于fieldStockLt.setValue("20");RuleV2 prule01_rule01 = new RuleV2();prule01_rule01.setId("prule01");prule01_rule01.setSubId("rule01");prule01_rule01.setFields(List.of(fieldCombIds, fieldStockGt, fieldStockLt));// 属性id-属性值id(普通键值对)RuleV2.Field fieldAttrIdToAttrValIdMap = new RuleV2.Field();fieldAttrIdToAttrValIdMap.setName(LambdaUtil.getFieldName(TestParam::getAttrIdToAttrValIdMap));fieldAttrIdToAttrValIdMap.setKey("10");fieldAttrIdToAttrValIdMap.setType(RuleFieldTypeEnum.NORMAL_KV);fieldAttrIdToAttrValIdMap.setCondition(RuleFieldConditionEnum.IN); // 包含fieldAttrIdToAttrValIdMap.setValue(List.of(11L, 12L));// 成分属性值id-属性数值RuleV2.Field fieldComponentAttrValIdToValMapGt = new RuleV2.Field();fieldComponentAttrValIdToValMapGt.setName(LambdaUtil.getFieldName(TestParam::getComponentAttrValIdToValMap));fieldComponentAttrValIdToValMapGt.setKey("10");fieldComponentAttrValIdToValMapGt.setType(RuleFieldTypeEnum.NUMBER_KV); // 数值键值对fieldComponentAttrValIdToValMapGt.setCondition(RuleFieldConditionEnum.GT); // 大于fieldComponentAttrValIdToValMapGt.setValue("10");RuleV2.Field fieldComponentAttrValIdToValMapLt = new RuleV2.Field();fieldComponentAttrValIdToValMapLt.setName(LambdaUtil.getFieldName(TestParam::getComponentAttrValIdToValMap));fieldComponentAttrValIdToValMapLt.setKey("10");fieldComponentAttrValIdToValMapLt.setType(RuleFieldTypeEnum.NUMBER_KV);fieldComponentAttrValIdToValMapLt.setCondition(RuleFieldConditionEnum.LT); // 小于fieldComponentAttrValIdToValMapLt.setValue("20");RuleV2 prule01_rule02 = new RuleV2();prule01_rule02.setId("prule01");prule01_rule02.setSubId("rule02");prule01_rule02.setFields(List.of(fieldAttrIdToAttrValIdMap, fieldComponentAttrValIdToValMapGt, fieldComponentAttrValIdToValMapLt));// 调用 RuleUtil 规则匹配方法List<RuleV2> matchResults = RuleUtilV2.match(param, List.of(prule01_rule01, prule01_rule02));// 命中组合字段的规则assertEquals(List.of(prule01_rule01, prule01_rule02), matchResults);}
}
三、RuleUtil 实践技巧
1、Rule 类的 param 字段可以 set 任何规则信息(如子规则配置的图片 id、枚举值等等);
2、若业务规则存在不同优先级,可以通过对 Rule 列表进行排序实现优先级匹配,排序靠前的优先匹配;
3、若存在命中规则的数量限制(比如优先或随机选择命中的第一个规则),可以通过入参 matchLimit 进行控制;
4、若业务场景对业务规则的更新不要求实时,可以对转换后的 Rule 数组进行缓存,降低数据库查询和转换次数,提高性能;
5、后续将逐步完善 RuleUtil 单元测试用例,保证核心匹配逻辑准确无误。
使用 VuePress + Github 30分钟搭建属于自己的AI网站
相关文章:
【RuleUtil】适用于全业务场景的规则匹配快速开发工具
一、RuleUtil 开发背景 1.1 越来越多,越来越复杂的业务规则 1、规则的应用场景多 2、规则配置的参数类型多(ID、数值、文本、日期等等) 3、规则的参数条件多(大于、小于、等于、包含、不包含、区间等等) 4、规则的结…...
多表查询之嵌套查询
目录 引言 一、标量嵌套查询 二、列嵌套查询 三、行嵌套查询 四、表嵌套查询 引言 1、概念 SQL语句中嵌套 select 语句,称为嵌套查询,又称子查询。嵌套查询外部的语句可以是 insert / update / delete / select 的任何一个。 嵌套…...
js原型链prototype解释
function Person(){} var personnew Person() console.log(啊啊,Person instanceof Function);//true console.log(,Person.__proto__Function.prototype);//true console.log(,Person.prototype.__proto__ Object.prototype);//true console.log(,Function.prototype.__prot…...
RK3588 ubuntu20禁用自带的TF卡挂载,并设置udev自动挂载
禁用系统的自动挂载(udisks2) sudo vim /etc/udev/rules.d/80-disable-automount.rules添加 ACTION"add", KERNEL"mmcblk1p1", ENV{UDISKS_IGNORE}"1"KERNEL“mmcblk1p1”:匹配设备名(TF卡通常是…...
【Pytorch 中的扩散模型】去噪扩散概率模型(DDPM)的实现
介绍 广义上讲,扩散模型是一种生成式深度学习模型,它通过学习到的去噪过程来创建数据。扩散模型有很多变体,其中最流行的通常是文本条件模型,它可以根据提示生成特定的图像。一些扩散模型(例如 Control-Net࿰…...
AR/VR衍射光波导性能提升遇阻?OAS光学软件有方法
衍射波导准直系统设计案例 简介 在现代光学显示技术中,衍射光波导系统因其独特的光学性能和紧凑的结构设计,在增强现实(AR)、虚拟现实(VR)等领域展现出巨大的应用潜力。本案例聚焦于衍射波导准直系统&…...
联易融受邀参加上海审计局金融审计处专题交流座谈
近日,联易融科技集团受邀出席了由上海市审计局金融审计处组织的专题交流座谈,凭借其在供应链金融领域的深厚积累和创新实践,联易融为与会人员带来了精彩的分享,进一步加深现场对供应链金融等金融发展前沿领域的理解。 在交流座谈…...
【中级软件设计师】程序设计语言基础成分
【中级软件设计师】程序设计语言基础成分 目录 【中级软件设计师】程序设计语言基础成分一、历年真题二、考点:程序设计语言基础成分1、基本成分2、数据成分3、控制成分 三、真题的答案与解析答案解析 复习技巧: 若已掌握【程序设计语言基础成分】相关知…...
高并发抢券系统设计与落地实现详解
📚 目录 一、业务背景与系统目标 二、架构设计总览 三、热点数据预热与缓存设计 四、抢券逻辑核心 —— Redis Lua 脚本 五、抢券接口实现要点 六、结果同步机制设计 七、性能优化策略 八、总结 在电商系统中,抢券作为一种典型的秒杀业务场景&a…...
外商在国内宣传 活动|发布会|参展 邀请媒体
传媒如春雨,润物细无声,大家好,我是51媒体胡老师。 外商在国内开展宣传活动、发布会或参展时,邀请媒体是扩大影响力、提升品牌知名度的关键环节。 一、活动筹备阶段:选择具有实力且更有性价比的媒体服务商(…...
物联网 (IoT) 安全简介
什么是物联网安全? 物联网安全是网络安全的一个分支领域,专注于保护、监控和修复与物联网(IoT)相关的威胁。物联网是指由配备传感器、软件或其他技术的互联设备组成的网络,这些设备能够通过互联网收集、存储和共享数据…...
大模型面经 | 春招、秋招算法面试常考八股文附答案(四)
大家好,我是皮先生!! 今天给大家分享一些关于大模型面试常见的面试题,希望对大家的面试有所帮助。 往期回顾: 大模型面经 | 春招、秋招算法面试常考八股文附答案(RAG专题一) 大模型面经 | 春招、秋招算法面试常考八股文附答案(RAG专题二) 大模型面经 | 春招、秋招算法…...
从零开始学习MySQL的系统学习大纲
文章目录 前言第一阶段:数据库与 MySQL 基础认知数据库基础概念MySQL 简介 第二阶段:MySQL 安装与环境搭建安装前的准备MySQL 安装过程安装后的配置 第三阶段:SQL 基础语法SQL 概述数据库操作数据表操作数据操作 第四阶段:SQL 高级…...
ycsb性能测试的优缺点
YCSB(Yahoo Cloud Serving Benchmark)是一个开源的性能测试框架,用于评估分布式系统的读写性能。它具有以下优点和缺点: 优点: 简单易用:YCSB提供了简单的API和配置文件,使得性能测试非常容易…...
Linux:简单自定义shell
1.实现原理 考虑下⾯这个与shell典型的互动: [rootlocalhost epoll]# ls client.cpp readme.md server.cpp utility.h [rootlocalhost epoll]# ps PID TTY TIME CMD 3451 pts/0 00:00:00 bash 3514 pts/0 00:00:00 ps ⽤下图的时间轴来表⽰事件的发⽣次序。其中时…...
Android Studio开发 SharedPreferences 详解
文章目录 SharedPreferences 详解基本概念获取 SharedPreferences 实例1. Context.getSharedPreferences()2. Activity.getPreferences()3. PreferenceManager.getDefaultSharedPreferences() 存储模式写入数据apply() vs commit() 读取数据监听数据变化最佳实践高级用法存储字…...
Qt基础006(事件)
文章目录 消息对话框QMessageBox快捷键开发基础 事件事件处理过程事件过滤器 消息对话框QMessageBox QMessageBox 是 Qt 框架中用于显示消息框的一个类,它常用于向用户显示信息、询问问题或者报告错 误。以下是 QMessageBox 的一些主要用途: 显示信息…...
Mediatek Android13 设置Launcher
概述: 本章将围绕Launcher讲述两种修改默认Launcher的情况。 一:完全覆盖 第一种方法和预置apk类似,区别在于增加LOCAL_OVERRIDES_PACKAGES说明,该方法会完全覆盖系统默认的Launcher。 关于如何预置apk,可见另一篇文章: Mediatek Android13 预置APP-CSDN博客 修改A…...
【Linux网络】构建基于UDP的简单聊天室系统
📢博客主页:https://blog.csdn.net/2301_779549673 📢博客仓库:https://gitee.com/JohnKingW/linux_test/tree/master/lesson 📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正! &…...
【微知】git reset --soft --hard以及不加的区别?
背景 在 Git 里,git reset 是用来将当前的 HEAD 复位到指定状态的命令。--soft、--hard 是它的两个常用选项,本文简单介绍他们的区别,以及不添加选项时的默认情况。 在 Git 里,HEAD 是一个重要的引用,它指向当前所在的…...
制作一个简单的操作系统7
实模式下到保护模式下并打印 运行效果: 完整代码: 【免费】制作一个简单的操作系统7的源代码资源-CSDN文库https://download.csdn.net/download/farsight_2098/90670296 从零开始写操作系统引导程序:实模式到保护模式的跨越 引言 操作系统的启动过程是计算机系统中最神…...
2025企微CRM系统功能对比:会话存档、客户画像与数据分析如何重构客户运营?
一、企微CRM管理系统:从“连接工具”到“智能中枢” 随着企业微信生态的成熟,企微CRM管理软件已从简单的客户沟通渠道,升级为融合数据、策略与服务的核心平台。2025年,企业对企微CRM系统的需求聚焦于三大能力:会话存档…...
【JAVA】十三、基础知识“接口”精细讲解!(二)(新手友好版~)
哈喽大家好呀qvq,这里是乎里陈,接口这一知识点博主分为三篇博客为大家进行讲解,今天为大家讲解第二篇java中实现多个接口,接口间的继承,抽象类和接口的区别知识点,更适合新手宝宝们阅读~更多内容持续更新中…...
小白工具视频转MPG, 功能丰富齐全,无需下载软件,在线使用,超实用
在视频格式转换需求日益多样的今天,小白工具网的在线视频转 MPG 功能https://www.xiaobaitool.net/videos/convert-to-mpg/ )脱颖而出,凭借其出色特性,成为众多用户处理视频格式转换的优质选择。 从格式兼容性来看,它支…...
#define RFOREACH(var, arr) for (ARR2IDX(arr) var=(arr).size(); var-->0; )
这个宏的定义: #define RFOREACH(var, arr) for (ARR2IDX(arr) var (arr).size(); var-- > 0; )是用来 反向遍历一个容器(比如 vector) 的,非常紧凑而且聪明的写法。 逐步解释一下: 假设你有一个容器,…...
MYSQL—两阶段提交
binlog 和 redo log: 有binlog了为什么还要有redo log: 历史原因,MyISAM不支持崩溃恢复,而InnoDB在加入MySQL前就已经支持崩溃恢复了InnoDB使用的是WAL技术,事务提交后,写完内存和日志,就算事…...
Qt之moveToThread
文章目录 前言一、基本概念1.1 什么是线程亲和性?1.2 moveToThread 的作用 二、使用场景三、使用方法四、使用示例五、注意事项六、常见问题总结 前言 moveToThread 是 Qt 中用于管理对象线程亲和性(Thread Affinity)的核心方法。它的作用是…...
Nacos 2.0.2 在 CentOS 7 上开启权限认证(含 Docker Compose 配置与接口示例)
介绍如何在 Nacos 2.0.2 CentOS 7 环境中开启权限认证,包括 解压部署 和 Docker Compose 部署 两种方式,提供客户端 Spring Boot 项目的接入配置和nacos接口验证示例。 环境说明 操作系统:CentOS 7Nacos 版本:2.0.2部署方式&…...
Oracle--SQL事务操作与管理流程
前言:本博客仅作记录学习使用,部分图片出自网络,如有侵犯您的权益,请联系删除 数据库系统的并发控制以事务为单位进行,通过内部锁定机制限制事务对共享资源的访问,确保数据并行性和一致性。事务是由一系列语…...
Qt -对象树
博客主页:【夜泉_ly】 本文专栏:【暂无】 欢迎点赞👍收藏⭐关注❤️ 目录 前言构造QObject::QObjectQObjectPrivate::setParent_helper 析构提醒 #mermaid-svg-FTUpJmKG24FY3dZY {font-family:"trebuchet ms",verdana,arial,sans-s…...
Unity 带碰撞的粒子效果
碰撞效果:粒子接触角色碰撞体弹起,粒子接触地面弹起。 粒子效果:粒子自行加速度下落,并且在接触碰撞体弹起时产生一个小的旋转。 *注意使用此效果时,自行判断是否需要调整碰撞层级。 以下为角色身高为1.7m时&#x…...
扩散模型(Diffusion Models)
扩散模型(Diffusion Models)是近年来在生成式人工智能领域崛起的一种重要方法,尤其在图像、音频和视频生成任务中表现突出。其核心思想是通过逐步添加和去除噪声的过程来学习数据分布,从而生成高质量样本。 核心原理 扩散…...
JSP服务器端表单验证
JSP服务器端表单验证 一、引言 在Web开发中,表单验证是保障数据合法性的重要环节。《Web编程技术》第五次实验要求,详细讲解如何基于JSP内置对象实现服务器端表单验证,包括表单设计、验证逻辑、交互反馈等核心功能。最终实现:输…...
Anaconda、conda和PyCharm在Python开发中各自扮演的角色
Anaconda、conda和PyCharm在Python开发中各自扮演不同角色,它们的核心用处、区别及相互关系如下: 一、Anaconda与conda的用处及区别 1. Anaconda - 定义:Anaconda是一个开源的Python和R语言发行版,专为数据科学、机器学习等场景…...
【数据结构 · 初阶】- 堆的实现
目录 一.初始化 二.插入 三.删除(堆顶、根) 四.整体代码 Heap.h Test.c Heap.c 我们使用顺序结构实现完全二叉树,也就是堆的实现 以前学的数据结构只是单纯的存储数据。堆除了存储数据,还有其他的价值——排序。是一个功能…...
Ubuntu与OpenHarmony OS 5.0显示系统架构比较
1. 总体架构对比 1.1 Ubuntu显示架构 Ubuntu采用传统Linux显示栈架构,自顶向下可分为: 应用层:GNOME桌面环境和应用程序显示服务器层:X11或Wayland图形栈中间层:Mesa, DRM/KMS硬件层:GPU驱动和硬件 1.2 …...
一键配置多用户VNC远程桌面:自动化脚本详解
在当今远程工作盛行的时代,高效且安全地管理多用户远程桌面访问变得至关重要。本文将介绍一个强大的自动化脚本,该脚本能够快速创建用户并配置VNC远程桌面环境,大大简化了系统管理员的工作。 一、背景介绍 在Linux系统中,手动配置VNC服务器通常需要执行多个步骤,包括创建…...
Qt进阶开发:鼠标及键盘事件
文章目录 一、Qt中事件的概念二、Qt中事件处理方式三、重新实现部件的事件处理函数3.1 常用事件处理函数3.2 自定义控件处理鼠标和绘图事件3.3 常用事件处理函数说明四、重写notify()函数五、QApplication对象上安装事件过滤器六、重写event()事件七、在对象上安装事件过滤器八…...
鸿蒙生态新利器:华为ArkUI-X混合开发框架深度解析
鸿蒙生态新利器:华为ArkUI-X混合开发框架深度解析 作者:王老汉 | 鸿蒙生态开发者 | 2025年4月 📢 前言:开发者们的新机遇 各位鸿蒙开发者朋友们,是否还在为多平台开发重复造轮子而苦恼?今天给大家介绍一位…...
VSCode 用于JAVA开发的环境配置,JDK为1.8版本时的配置
插件安装 JAVA开发在VSCode中,需要安装JAVA的必要开发。当前安装只需要安装 “Language Support for Java(TM) by Red Hat”插件即可 安装此插件后,会自动安装包含如下插件,不再需要单独安装 Project Manager for Java Test Runner for J…...
Git Flow分支模型
经典分支模型(Git Flow) 由 Vincent Driessen 提出的 Git Flow 模型,是管理 main(或 master)和 dev 分支的经典方案: main 用于生产发布,保持稳定; dev 用于日常开发,合并功能分支(feature/*); 功能开发在 feature 分支进行,完成后合并回 dev; 预发布分支(rele…...
机器人进阶---视觉算法(六)傅里叶变换在图像处理中怎么用
傅里叶变换在图像处理中怎么用 傅里叶变换的基本原理应用场景Python代码示例逐行解释总结傅里叶变换在图像处理中是一种重要的工具,它将图像从空间域转换到频域,从而可以对图像的频率特性进行分析和处理。傅里叶变换在图像滤波、图像增强、图像压缩和图像分析等方面都有广泛应…...
Linux-skywalking部署步骤并且添加探针
skywalking部署步骤 上传skywalking安装包并解压将skywalking安装包apache-skywalking-apm-10.1.0.tar.gz上传到服务器/data目录下 用解压命令解压 cd /data tar -xvf apache-skywalking-apm-10.1.0.tar.gz 解压后重名目录 mv apache-skywalking-apm-bin skywalking 上传…...
开启报名!火山引擎 x PICO-全国大学生物联网设计竞赛赛题发布
全国大学生物联网设计竞赛(以下简称“竞赛”)是教育部高等学校计算机类专业教学指导委员会创办的物联网领域唯一的学科竞赛,是以学科竞赛推动专业建设、培养大学生创新能力为目标,面向高校大学生举办的全国性赛事。自 2014 年开始…...
【遥感科普】光谱分辨率是什么?
光谱分辨率是指传感器或光谱仪器在电磁波谱中区分相邻波长或频率的能力。它反映了设备对光谱细节的捕捉能力,通常用波长间隔(如纳米,nm)或波数(cm⁻)表示。例如,若光谱分辨率为10 nm,…...
Trae国内版怎么用?Trae IDE 内置 MCP 市场配置使用指南
近日,字节跳动旗下Trae IDE发布了全新版本,新版本中,Trae IDE 的自定义智能体能力让 AI 能够基于开发者需求灵活调度多维度的工具和资源,从而为任务提供全方位的支持,只需一下即可召唤智能体,这个过程中&am…...
Javase 基础入门 —— 02 基本数据类型
本系列为笔者学习Javase的课堂笔记,视频资源为B站黑马程序员出品的《黑马程序员JavaAI智能辅助编程全套视频教程,java零基础入门到大牛一套通关》,章节分布参考视频教程,为同样学习Javase系列课程的同学们提供参考。 01 注释 单…...
模型 螃蟹效应
系列文章分享模型,了解更多👉 模型_思维模型目录。个体互钳,团队难行。 1 螃蟹效应的应用 1.1 教育行业—优秀教师遭集体举报 行业背景:某市重点中学推行绩效改革,将班级升学率与教师奖金直接挂钩,打破原…...
597页PPT丨流程合集:流程梳理方法、流程现状分析,流程管理规范及应用,流程绩效的管理,流程实施与优化,流程责任人的角色认知等
流程梳理是通过系统化分析优化业务流程的管理方法,其核心包含四大步骤:①目标确认,明确业务痛点和改进方向;②现状分析,通过流程图、价值流图还原现有流程全貌,识别冗余环节和瓶颈节点;③优化设…...
Kotlin集合全解析:List和Map高频操作手册
Kotlin 中 Map 和 List 常用功能总结 List 常用功能 创建 List val immutableList listOf(1, 2, 3) // 不可变列表 val mutableList mutableListOf("a", "b", "c") // 可变列表 val emptyList emptyList<String>() // 空列表基本…...