需求
在我们的流程设计中,用户执行抽奖时会判断是否已经超过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;}@Overridepublic 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())) {// 黑名单返回固定的奖品IDreturn 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{@Resourceprivate DefaultLogicFactory logicFactory;public DefaultRaffleStrategy(IStrategyRepository repository, IStrategyDispatch strategyDispatch) {super(repository, strategyDispatch);}@Overrideprotected 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`),则立即返回过滤结果。
最后,如果所有规则都通过了过滤,方法将返回最后一个规则的过滤结果。