背景
去年底换了公司,目前在做HR招聘系统,产品要求:管理端手动配置发布招聘要求(支持多个招聘条件AND与OR关系编排);当C端用户报名该职位时,系统执行该规则,返回报名成功或失败。根据技术调研,采用db存规则json串。然后报名时,执行该规则的方法。
前端样式

招聘岗位的多个规则关系是OR关系;同1个条件点击+号,可增加多个规则,是AND条件
代码入口

比如上述单元测试的场景:招java开发岗,要求:满足本科+35岁以下或本科+P7以上即可。
执行单测:
通过日志就能清晰的分析出,岗位配了什么规则,各规则的执行结果是什么

代码设计
类图
通过工厂+策略的设计模式,实现该规则,符合开闭原则,方便后续业务规则扩充,代码扩展

代码介绍
核心接口:InternalPositionRule 定义了规则执行的统一方法。
抽象类:AbstractPositionRule 提供了通用的比较和告警逻辑。
具体规则类:如 AgeRule、SexRule 等,继承自 AbstractPositionRule。
规则包装类:RuleWrapper 用于封装规则信息并动态创建规则实例。
规则组和规则集:RuleGroup 和 RuleSet 用于组织规则的逻辑关系(AND 和 OR)。
规则上下文:PositionRuleContext 是规则引擎的统一入口。
规则工厂:PositionRuleFactory 用于动态创建规则实例。
员工类:Employee 包含员工的基本信息,用于规则匹配。
核心代码
以下是脱敏后的核心代码
InternalPositionRule 岗位规则 接口 最顶层抽象,制定模版方法
* +-----------------------------------+
* | InternalPositionRule (接口) |
* |-----------------------------------|
* | + execute(Employee emp): boolean |
* +-----------------------------------+
* ^
* |
* |
* +-----------------------------------+
* | AbstractPositionRule<T> (抽象类) |
* |-----------------------------------|
* | + compare(String, T, T): boolean |
* |-----------------------------------|
* | - ruleValue: String |
* | - forceFlag: String |
* | - compareSymbol: String |
* +-----------------------------------+
* ^
* |
* |
* +-----------------------------------+
* | AgeRule |
* |-----------------------------------|
* | + execute(Employee emp): boolean |
* +-----------------------------------+
**/
public interface InternalPositionRule {
/**
* 执行岗位条件规则
*
* @param emp 员工信息
* @return true/false
*/
boolean execute(Employee emp);
AbstractPositionRule 岗位规则 抽象类:业务上再扩展规则,继承该类即可
@Slf4j
@Component
public abstract class AbstractPositionRule<T extends Comparable<? super T>> implements InternalPositionRule {
/**
* 执行岗位条件规则
*
* @param emp 员工信息
* @return
*/
public abstract boolean execute(Employee emp);
/**
* 比较方法
*
* @param compareSymbol 比较符号
* @param actualValue 实际值
* @param thresholdValue 设定阈值
* @return
*/
public boolean compare(String compareSymbol, T actualValue, T thresholdValue) {
boolean result;
log.info("===比较开始,比较符号【{}】,配置阈值【{}】,实际值【{}】===", compareSymbol, thresholdValue, actualValue);
//等于
if (SymbolConstant.EQ.equalsIgnoreCase(compareSymbol)) {
result = CompareUtil.equals(actualValue, thresholdValue);
//大于
} else if (SymbolConstant.GT.equalsIgnoreCase(compareSymbol)) {
result = CompareUtil.gt(actualValue, thresholdValue);
//小于
} else if (SymbolConstant.LT.equalsIgnoreCase(compareSymbol)) {
result = CompareUtil.lt(actualValue, thresholdValue);
//大于等于
} else if (SymbolConstant.GE.equalsIgnoreCase(compareSymbol)) {
result = CompareUtil.ge(actualValue, thresholdValue);
//小于等于
} else if (SymbolConstant.LE.equalsIgnoreCase(compareSymbol)) {
result = CompareUtil.le(actualValue, thresholdValue);
} else {
log.error(StrUtil.join("当前类型:", compareSymbol, InternalErrorCode.TYPE_UNSUPPORTED_ERROR.getMsg()));
throw new ServiceException(InternalErrorCode.TYPE_UNSUPPORTED_ERROR);
}
log.info("==比较结束,结果【{}】===", result);
return result;
}
/**
* TODO 产品给出告警文案?怎么触达用户?是否持久化落库?
* 告警逻辑:要求如果规则配置了告警,即使匹配失败,也允许通过,并返回告警文案
*
* @param compareResult 实际匹配结果
* @param forceFlag 禁止或告警
* @param ruleTypeEnum 规则类型
* @return
*/
public Optional<String

最低0.47元/天 解锁文章
1万+

被折叠的 条评论
为什么被折叠?



