需求
在我们的流程设计中,用户执行抽奖时会判断是否已经超过N积分,如果超过N积分则可以在限定范围内进行抽奖。同时如果用户是黑名单范围的羊毛党用户,则只返回固定的奖品ID
模型
- 整个规则来说,分为抽奖前、抽奖中、抽奖后,三个阶段执行。本节我们先来处理抽奖前的规则。
- 在工程分包上,需要添加 rule 来处理抽奖规则,在添加 raffle 处理抽奖过程。
前置规则判断根据现有的积分决定能抽到什么,以及黑名单
新增实体
RaffleAwardEntity:抽奖结束后要返回一个奖品直接返回一些key交给后续Award有关的列进行处理
RaffleFactorEntity:抽奖因子(参数)抽奖策略接口的参数
RaffleActionEntity:返回一个对象实体,用于你抽奖前后的操作,如抽奖之前是否有解锁,
分为抽奖前过滤规则,抽奖中过滤规则,抽奖后过滤规则(如抽到了但未解锁,返回随机积分)
定义了一个名为RuleActionEntity
的泛型类,其中泛型类型T
必须是RuleActionEntity.RaffleEntity
的子类。同时,该类内部还定义了四个静态内部类:RaffleEntity
、RaffleBeforeEntity
、RaffleCenterEntity
和RaffleAfterEntity
,它们都是RaffleEntity
的子类。
这些类用于表示不同的抽奖阶段或状态。,RaffleBeforeEntity
表示抽奖开始前的状态,RaffleCenterEntity
表示抽奖进行中的状态,而RaffleAfterEntity
表示抽奖结束后的状态。
重新回答
||
RuleMatterRntity: 用于过滤和过滤接口配合使用
过滤接口
抽奖策略接口——传入因子放回奖品key
RaffleActionEntity:放回一些要用到的参数如权重值策略id还有用于黑名单的奖品id100
@Getter
@AllArgsConstructor
public enum RuleLogicCheckTypeVO {
ALLOW("0000", "放行;执行后续的流程,不受规则引擎影响"),
TAKE_OVER("0001","接管;后续的流程,受规则引擎执行结果影响"),
;
private final String code;
private final String info;
}
控制如何进行下一步这是一个Java枚举类,名为RuleLogicCheckTypeVO。它有两个枚举值:ALLOW和TAKE_OVER。每个枚举值都有一个对应的code和info属性。这个枚举类用于表示规则引擎的逻辑检查类型
public interface ILogicFilter<T extends RuleActionEntity.RaffleEntity> {
RuleActionEntity<T> filter(RuleMatterEntity ruleMatterEntity);
}
这里抽奖规则过滤接口 用RuleActionEntity要注意基础应为RuleActionEntity规定了,这样就可以返回一个RuleActionEntity的对象,具体是什么取决于具体场景
建立一个工厂方便规则,以及接口实现 annotation方便注入
完整代码详细理解
package org.example.domain.strategy.model.entity;
import lombok.*;
import org.example.domain.strategy.model.vo.RuleLogicCheckTypeVO;
/**
* @author xy
* @description 返回一个对象实体,用于返回之后的操作
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class RuleActionEntity<T extends RuleActionEntity.RaffleEntity> {
private String code= RuleLogicCheckTypeVO.ALLOW.getCode();
private String info=RuleLogicCheckTypeVO.ALLOW.getInfo();
private String ruleModel;
private T data;
static public class RaffleEntity{
}
@EqualsAndHashCode(callSuper = true)
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
/**抽奖前*/
static public class RaffleBeforeEntity extends RaffleEntity{
/**策略id*/
private Long strategyId;
/**权重值:用于抽奖可以选择权重*/
private String ruleWeightValueKey;
/**奖品ID*/
private Integer awardId;
}
/**抽奖中*/
static public class RaffleCenterEntity extends RaffleEntity{
}
/**抽奖后*/
static public class RaffleAfterEntity extends RaffleEntity{
}
}
包含了一些属性和方法。这个类使用了Lombok库来简化代码,提供了@Data、@AllArgsConstructor、@NoArgsConstructor和@Builder注解。这个类还定义了一个内部类RaffleEntity,以及三个继承自RaffleEntity的内部类:RaffleBeforeEntity、RaffleCenterEntity和RaffleAfterEntity。这些内部类分别表示抽奖前、抽奖中和抽奖后的操作。还规定了泛型,还有code和info用来输出到控制台以及后续的筛查
@EqualsAndHashCode(callSuper = true) //,用于自动生成 equals 和 hashCode 方法。当一个类继承了另一个类时, // 如果想让子类的 equals 和 hashCode 方法同时考虑父类的属性,就需要在子类上使用这个注解 //将callSuper属性设置为true。这样,在生成的equals和hashCode方法中,会先调用父类的相应方法,然后再考虑子类的字段
/**
* @author xy
* @description
*/
@Slf4j
public abstract class AbstractRaffleStrategy implements IRaffleStrategy {
//策略仓储服务-》domain层像一个大厨,仓储层提供米面油盐
protected IStrategyRepository repository;
//策略调度服务-》只负责抽奖处理,通过调用接口的方式,隔离职责,不需要使用方关心或者调用抽奖的初始化
protected IStrategyDispatch strategyDispatch;
public AbstractRaffleStrategy(IStrategyRepository repository, IStrategyDispatch strategyDispatch) {
this.repository = repository;
this.strategyDispatch = strategyDispatch;
}
@Override
public RaffleAwardEntity performRaffle(RaffleFactorEntity raffleFactorEntity) {
// 1. 参数校验
String userId = raffleFactorEntity.getUserId();
Long strategyId = raffleFactorEntity.getStrategyId();
if (null == strategyId || StringUtils.isBlank(userId)) {
throw new AppException(ResponseCode.ILLEGAL_PARAMETER.getCode(), ResponseCode.ILLEGAL_PARAMETER.getInfo());
}
// 2. 策略查询 得到了 id 模型 即strategy策略表的内容
StrategyEntity strategy = repository.queryStrategyEntityByStrategyId(strategyId);
// 3. 抽奖前 - 规则过滤
RuleActionEntity<RuleActionEntity.RaffleBeforeEntity> ruleActionEntity = this.doCheckRaffleBeforeLogic(RaffleFactorEntity.builder().userId(userId).strategyId(strategyId).build(), strategy.ruleModels());
if (RuleLogicCheckTypeVO.TAKE_OVER.getCode().equals(ruleActionEntity.getCode())) {
if (DefaultLogicFactory.LogicModel.RULE_BLACKLIST.getCode().equals(ruleActionEntity.getRuleModel())) {
// 黑名单返回固定的奖品ID
return RaffleAwardEntity.builder()
.awardId(ruleActionEntity.getData().getAwardId())
.build();
} else if (DefaultLogicFactory.LogicModel.RULE_WIGHT.getCode().equals(ruleActionEntity.getRuleModel())) {
// 权重根据返回的信息进行抽奖
RuleActionEntity.RaffleBeforeEntity raffleBeforeEntity = ruleActionEntity.getData();
String ruleWeightValueKey = raffleBeforeEntity.getRuleWeightValueKey();
Integer awardId = strategyDispatch.getRandomAwardId(strategyId, ruleWeightValueKey);
return RaffleAwardEntity.builder()
.awardId(awardId)
.build();
}
}
// 4. 默认抽奖流程
Integer awardId = strategyDispatch.getRandomAwardId(strategyId);
return RaffleAwardEntity.builder()
.awardId(awardId)
.build();
}
protected abstract RuleActionEntity<RuleActionEntity.RaffleBeforeEntity> doCheckRaffleBeforeLogic(RaffleFactorEntity build, String ...logics);
}
AbstractRaffleStrategy
,实现了IRaffleStrategy
接口。它主要用于处理抽奖策略的逻辑。在这个类中,定义了一些成员变量,如repository
和strategyDispatch
,分别用于存储策略数据和执行抽奖操作。同时,提供了一个构造函数,用于初始化这两个成员变量。
performRaffle
方法是这个类的核心方法,用于执行抽奖操作。它首先对输入参数进行校验,然后查询策略信息,接着根据策略规则进行过滤。如果满足某些条件,可以直接返回奖品ID;否则,按照默认的抽奖流程进行抽奖。最后,返回抽奖结果。
在这个Java代码中,使用protected
关键字来修饰成员变量(如repository
和strategyDispatch
)和方法(如doCheckRaffleBeforeLogic
)protected
关键字允许子类和同一个包中的其他类访问这些成员。这意味着,当我们创建一个继承自AbstractRaffleStrategy
的子类时,这个子类可以直接访问父类中的protected
成员,而无需通过公共接口(即public
方法)。
package org.example.domain.strategy.service.raffle;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.example.domain.strategy.model.entity.RaffleFactorEntity;
import org.example.domain.strategy.model.entity.RuleActionEntity;
import org.example.domain.strategy.model.entity.RuleMatterEntity;
import org.example.domain.strategy.model.vo.RuleLogicCheckTypeVO;
import org.example.domain.strategy.repository.IStrategyRepository;
import org.example.domain.strategy.service.amory.IStrategyDispatch;
import org.example.domain.strategy.service.rule.ILogicFilter;
import org.example.domain.strategy.service.rule.factory.DefaultLogicFactory;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* @author xy
* @description
*/
@Slf4j
@Service
public class DefaultRaffleStrategy extends AbstractRaffleStrategy{
@Resource
private DefaultLogicFactory logicFactory;
public DefaultRaffleStrategy(IStrategyRepository repository, IStrategyDispatch strategyDispatch) {
super(repository, strategyDispatch);
}
@Override
protected RuleActionEntity<RuleActionEntity.RaffleBeforeEntity> doCheckRaffleBeforeLogic(RaffleFactorEntity raffleFactorEntity, String... logics) {
Map<String, ILogicFilter<RuleActionEntity.RaffleBeforeEntity>> logicFilterGroup = logicFactory.openLogicFilter();
// 黑名单规则优先过滤
String ruleBackList = Arrays.stream(logics)
.filter(str -> str.contains(DefaultLogicFactory.LogicModel.RULE_BLACKLIST.getCode()))
.findFirst()
.orElse(null);
if (StringUtils.isNotBlank(ruleBackList)) {
ILogicFilter<RuleActionEntity.RaffleBeforeEntity> logicFilter = logicFilterGroup.get(DefaultLogicFactory.LogicModel.RULE_BLACKLIST.getCode());
RuleMatterEntity ruleMatterEntity = new RuleMatterEntity();
ruleMatterEntity.setUserId(raffleFactorEntity.getUserId());
ruleMatterEntity.setAwardId(ruleMatterEntity.getAwardId());
ruleMatterEntity.setStrategyId(raffleFactorEntity.getStrategyId());
ruleMatterEntity.setRuleModel(DefaultLogicFactory.LogicModel.RULE_BLACKLIST.getCode());
RuleActionEntity<RuleActionEntity.RaffleBeforeEntity> ruleActionEntity = logicFilter.filter(ruleMatterEntity);
if (!RuleLogicCheckTypeVO.ALLOW.getCode().equals(ruleActionEntity.getCode())) {
return ruleActionEntity;
}
}
// 顺序过滤剩余规则
List<String> ruleList = Arrays.stream(logics)
.filter(s -> !s.equals(DefaultLogicFactory.LogicModel.RULE_BLACKLIST.getCode()))
.collect(Collectors.toList());
RuleActionEntity<RuleActionEntity.RaffleBeforeEntity> ruleActionEntity = null;
for (String ruleModel : ruleList) {
ILogicFilter<RuleActionEntity.RaffleBeforeEntity> logicFilter = logicFilterGroup.get(ruleModel);
RuleMatterEntity ruleMatterEntity = new RuleMatterEntity();
ruleMatterEntity.setUserId(raffleFactorEntity.getUserId());
ruleMatterEntity.setAwardId(ruleMatterEntity.getAwardId());
ruleMatterEntity.setStrategyId(raffleFactorEntity.getStrategyId());
ruleMatterEntity.setRuleModel(ruleModel);
ruleActionEntity = logicFilter.filter(ruleMatterEntity);
// 非放行结果则顺序过滤
log.info("抽奖前规则过滤 userId: {} ruleModel: {} code: {} info: {}", raffleFactorEntity.getUserId(), ruleModel, ruleActionEntity.getCode(), ruleActionEntity.getInfo());
if (!RuleLogicCheckTypeVO.ALLOW.getCode().equals(ruleActionEntity.getCode())) return ruleActionEntity;
}
return ruleActionEntity;
}
}
这段代码是一个名为`DefaultRaffleStrategy`的Java类,它继承了`AbstractRaffleStrategy`抽象类。这个类主要用于处理抽奖策略的逻辑。在这个类中,定义了一个名为`doCheckRaffleBeforeLogic`的方法,该方法接收一个`RaffleFactorEntity`对象和一个可变参数`logics`,用于检查抽奖前的逻辑。
方法首先创建一个名为`logicFilterGroup`的映射,用于存储不同类型的逻辑过滤器。然后,它会检查传入的`logics`数组中是否包含黑名单规则(`RULE_BLACKLIST`),如果包含,则使用相应的逻辑过滤器对用户进行过滤。如果过滤结果不是允许(`ALLOW`),则直接返回过滤结果。
接下来,方法会过滤剩余的规则,并依次应用这些规则。对于每个规则,它会从`logicFilterGroup`中获取相应的逻辑过滤器,然后使用该过滤器对用户进行过滤。如果过滤结果不是允许(`ALLOW`),则立即返回过滤结果。
最后,如果所有规则都通过了过滤,方法将返回最后一个规则的过滤结果。