大营销平台 —— 抽奖前置规则过滤

一、前言

上一期我们实现了权重配置的装配,这一期我们将实现抽奖业务的前置规则过滤 ,这里主要涉及了两个规则,第一个是黑名单(要求这些用户100积分只能抽到1积分),第二个是权重抽奖(在幸运值达到指定分数时,触发保底机制),为什么叫做前置过滤呢,因为黑名单和权重都需要在每次抽奖前就判断好,也就类似于一个拦截器。

我认为在实现这些复杂业务的时候应该遵循"找核心"的思维,从整体推到局部,并且应该先确定整体的核心类,然后再确定核心类中的核心方法,接着确定核心方法中的核心流程,最后再去看想要实现这个流程需要补充哪些实体/工厂/实现类。

二、核心流程抽象类

这里的基本准备很重要,后面的规则过滤都需要通过这些准备内容来实现,我们在这里需要准备两部分内容:

1.过滤器的整体框架(接口)

2.各种实体类

1. 核心类AbstractRaffleStrategy

我们以往写对请求的过滤器时我们看到的preHandle/postHandle这些方法其实就是前置/后置过滤,当然,我们这里不能使用这个,因为我们不是对请求拦截,只是在抽奖业务内部对业务规则进行拦截,所以我们在这里选择创建一个抽象类来充当一个过滤器框架

java 复制代码
public interface HandlerInterceptor {
    // 在 Controller 方法执行前调用
    boolean preHandle(HttpServletRequest request, 
                     HttpServletResponse response, 
                     Object handler) throws Exception;
    
    // 在 Controller 方法执行后,视图渲染前调用
    void postHandle(...);
    
    // 在整个请求结束后调用
    void afterCompletion(...);
}

首先要明确整体流程,这个是最重要的,视频中在这部分就讲得不好,如果结构没有理清楚,后面基本上都是在抄代码。

整体流程:

1.开始抽奖 ------ 从抽象类AbstractRaffleStrategy中开始

2.校验参数

3.过滤

4.在过滤时检验userId是否在黑名单,在的话就返回一个固定的奖品

5.如果不在就再去检验有没有权重规则(检查幸运值

搞清楚流程过后就可以开始写代码了,首先应该先写出刚刚我们看到的类似于请求拦截器的过滤结构框架,然后再在其中的抽奖前过滤方法中增加过滤逻辑

首先要注意,我们是要创建一个抽象类,确实很久没有写过抽象类了,一直以来都是通过实现接口的方式来规定方法,在这里回顾一下抽象类的作用:

抽象类的存在就是为了让子类继承的,子类会继承抽象父类的所有非私有方法。

这里的这个抽奖策略抽象类是本小节的核心类,它规定了整个抽奖的流程,也就是建立了一个抽奖框架。

java 复制代码
/**
 * @author 印东升
 * @description 抽奖策略抽象类,定义抽奖的标准流程
 * @create 2026-04-10 11:19
 */
@Slf4j
public abstract class AbstractRaffleStrategy implements IRaffleStrategy {

    //策略仓储服务
    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. 策略查询
        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 raffleFactorEntity, String... logics);
}

2.核心方法performRaffle()

我们必须好好分析这个类,必须理解它每一部分在干什么,因为这个类是核心

首先,performRaffle() 是这个抽象类中最核心的方法是用于进行抽奖前置过滤的方法 ,我们需要传入userId和strategyId (封装到了一个RaffleFactorEntity 实体类),用于校验参数,因为我们需要知道当前的userId才能知道其是否在黑名单之中,同时我们需要知道当前的strategyId,用于获取当前策略的所有规则(比如这里的100001号策略里面就有两个规则,一个是黑名单,一个是权重规则,后续要一个一个地去过滤它们)

java 复制代码
 public RaffleAwardEntity performRaffle(RaffleFactorEntity raffleFactorEntity)

然后我们来看看这个核心方法里面的流程:

1.参数校验 ,从传入的raffleFactorEntity 对象中获取我们想要的userIdstrategyId,判空:

java 复制代码
//抽奖因子实体
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class RaffleFactorEntity {
    /**
     * 用户id
     */
    private String userId;
    /**
     * 策略id
     */
    private Long strategyId;
}
java 复制代码
        // 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.查询策略,这里查询策略实体是为了获取策略,本质还是通过从表中获取策略规则ruleModels

(这里调用仓储,然后仓储再调用Dao,最后Dao查表)

java 复制代码
 // 2. 策略查询
StrategyEntity strategy = repository.queryStrategyEntityByStrategyId(strategyId);
3.核心流程:规则过滤
java 复制代码
        // 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();
            }
        }

这里就是核心方法中的核心流程了,接下来一步一步来拆开理解;

核心流程分为四步:

java 复制代码
 RuleActionEntity<RuleActionEntity.RaffleBeforeEntity> ruleActionEntity = this.doCheckRaffleBeforeLogic(RaffleFactorEntity.builder().userId(userId).strategyId(strategyId).build(), strategy.ruleModels());

1.核心流程的第一步 中我们定义了一个新的实体类RuleActionEntity ,这个实体类结构有点复杂,但是目的其实就是通过泛型和内部静态类来做到一个大实体类中含多个用途实体类。

再说详细一点:也就是这一个实体类中有三个用途,分别是前中后过滤,那么我们就写三个内部静态类,通过泛型我们就可以知道当前是用的哪个实体类,这样的好处是可以将这三个用途的实体类放在一起统一管理,并且它们三个用途实体类的一些共有的属性可以不用重复写了,直接写在大实体类中就行了。

而这个前置用途实体类里面放什么呢?就放我们前置过滤需要用的属性就行了,所以有7个属性:code、info、ruleModel、data(共有)和strategyId、ruleWeightValueKey、awardId(前置)

这里调用了doCheckRaffleBeforeLogic()方法,暂时按下不表,直接理解为

java 复制代码
//规则动作实体
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
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
    @Builder
    @AllArgsConstructor
    @NoArgsConstructor
    static public class RaffleBeforeEntity extends RaffleEntity {
        /**
         * 策略id
         */
        private Long strategyId;
        /**
         * 权重key:用于抽奖时可以选择权重抽奖
         */
        private String ruleWeightValueKey;
        /**
         * 奖品id
         */
        private Integer awardId;
    }

    //抽奖之中
    static public class RaffleCenterEntity extends RaffleEntity {}
    //抽奖之后
    static public class RaffleAfterEntity extends RaffleEntity {}
}
java 复制代码
 if (RuleLogicCheckTypeVO.TAKE_OVER.getCode().equals(ruleActionEntity.getCode()))

2.核心流程第第二步 时我们已经拿到了规则动作实体对象了,现在要做的是判断是放行还是接管,如果放行,就直接走默认抽奖了,如果接管就要开始过滤规则了。

java 复制代码
//规则过滤校验类型值对象
@Getter
@AllArgsConstructor
public enum RuleLogicCheckTypeVO {

    ALLOW("0000", "放行:执行后续的流程,不受规则引擎影响"),
    TAKE_OVER("0001", "接管:后续的流程,受规则引擎执行结果影响"),
    ;
    private final String code;
    private final String info;
}
bash 复制代码
-----------------------------------------分割线-------------------------------------------------
java 复制代码
if (DefaultLogicFactory.LogicModel.RULE_BLACKLIST.getCode().equals(ruleActionEntity.getRuleModel())) {
    // 黑名单返回固定的奖品ID
    return RaffleAwardEntity.builder()
            .awardId(ruleActionEntity.getData().getAwardId())
            .build();
}

3.核心流程第三步:进行黑名单过滤, 这里涉及到了规则工厂的事情了,这里面的代码看着很复杂,还涉及到了注解,其实不要想复杂了,这个工厂的核心作用是映射 ,我们创建了一个注解LogicStrategy这个注解专门标记规则的实现类 (标记后让Spring放到容器里面),这个工厂就是将容器中的规则过滤实现类 整理到一个map中(比如黑名单和权重规则)。

那么显然的,当命中了黑名单规则,我们就写死一个奖品返回给用户。

java 复制代码
//规则工厂
@Service
public class DefaultLogicFactory {

    public Map<String, ILogicFilter<?>> logicFilterMap = new ConcurrentHashMap<>();

    public DefaultLogicFactory(List<ILogicFilter<?>> logicFilters) {
        logicFilters.forEach(logic -> {
            LogicStrategy strategy = AnnotationUtils.findAnnotation(logic.getClass(), LogicStrategy.class);
            if (null != strategy) {
                logicFilterMap.put(strategy.logicMode().getCode(), logic);
            }
        });
    }

    public <T extends RuleActionEntity.RaffleEntity> Map<String, ILogicFilter<T>> openLogicFilter() {
        return (Map<String, ILogicFilter<T>>) (Map<?, ?>) logicFilterMap;
    }

    @Getter
    @AllArgsConstructor
    public enum LogicModel {

        RULE_WIGHT("rule_weight","【抽奖前规则】根据抽奖权重返回可抽奖范围KEY"),
        RULE_BLACKLIST("rule_blacklist","【抽奖前规则】黑名单规则过滤,命中黑名单则直接返回"),
        ;
        private final String code;
        private final String info;
    }

}
java 复制代码
//策略自定义枚举
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface LogicStrategy {

    DefaultLogicFactory.LogicModel logicMode();
}
java 复制代码
-----------------------------------------分割线-----------------------------------------------
java 复制代码
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.核心流程第四步:权重规则过滤,**这里比较好理解,就是拿到指定权重的权重值,比如:4000:101,102,103,然后调用前面一节课的权重概率装配,获取权重配置中的随机奖品,最后返回给用户。

java 复制代码
-----------------------------------------分割线-----------------------------------------------

最后就是规则都没有被命中,就直接走默认的抽奖流程了,很好理解。

java 复制代码
        // 4. 默认抽奖流程
        Integer awardId = strategyDispatch.getRandomAwardId(strategyId);

        return RaffleAwardEntity.builder()
                .awardId(awardId)
                .build();

别忘了在核心类里面还有一个方法,这个方法我们放下一个板块讲:

java 复制代码
    protected abstract RuleActionEntity<RuleActionEntity.RaffleBeforeEntity> doCheckRaffleBeforeLogic(RaffleFactorEntity raffleFactorEntity, String... logics);

三、规则过滤的实现类

1.核心子类DefaultRaffleStrategy

接下来的两个类就是刚刚提到的被注解标记的过滤规则的实现类了,你可能会有疑惑,我怎么感觉没用到这个实现类呢? 其实是因为我们还没有讲刚刚提到的**doCheckRaffleBeforeLogic()**方法,这个方法是protected的,也就是是供子类重写的,所以我们真正执行这个流程的不是刚刚的核心抽象类,而是核心抽象类的子类------DefaultRaffleStrategy 。

java 复制代码
@Service
@Slf4j
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;
    }
}

这里面就可以很容易地看到它实现了doCheckRaffleBeforeLogic()方法,这里面就把刚刚注解那里讲的------被Spring放到容器中的实现类map给取出来了,取出来了然后就可以按照优先级顺序处理了。

两个规则过滤实现类如下:

2.黑名单过滤规则实现类

java 复制代码
/**
 * @author 印东升
 * @description 【抽奖前规则】黑名单用户过滤规则
 * @create 2026-04-10 11:53
 */
@Slf4j
@Component
@LogicStrategy(logicMode = DefaultLogicFactory.LogicModel.RULE_BLACKLIST)
public class RuleBlackListLogicFilter implements ILogicFilter<RuleActionEntity.RaffleBeforeEntity> {

    @Resource
    private IStrategyRepository repository;

    @Override
    public RuleActionEntity<RuleActionEntity.RaffleBeforeEntity> filter(RuleMatterEntity ruleMatterEntity) {
        log.info("规则过滤-黑名单 userId:{} strategyId:{} ruleModel:{}", ruleMatterEntity.getUserId(), ruleMatterEntity.getStrategyId(), ruleMatterEntity.getRuleModel());
        String userId = ruleMatterEntity.getUserId();

        String ruleValue = repository.queryStrategyRuleValue(ruleMatterEntity.getStrategyId(),ruleMatterEntity.getAwardId(),ruleMatterEntity.getRuleModel());
        String[] splitRuleValue = ruleValue.split(Constants.COLON);
        Integer awardId = Integer.parseInt(splitRuleValue[0]);

        //100:user001,user002,user003
        // 过滤其他规则
        String[] userBlackIds = splitRuleValue[1].split(Constants.SPLIT);
        for (String userBlackId : userBlackIds) {
            if (userId.equals(userBlackId)) {
                return RuleActionEntity.<RuleActionEntity.RaffleBeforeEntity>builder()
                        .ruleModel(DefaultLogicFactory.LogicModel.RULE_BLACKLIST.getCode())
                        .data(RuleActionEntity.RaffleBeforeEntity.builder()
                                .strategyId(ruleMatterEntity.getStrategyId())
                                .awardId(awardId)
                                .build())
                        .code(RuleLogicCheckTypeVO.TAKE_OVER.getCode())
                        .info(RuleLogicCheckTypeVO.TAKE_OVER.getInfo())
                        .build();
            }
        }

        return RuleActionEntity.<RuleActionEntity.RaffleBeforeEntity>builder()
                .code(RuleLogicCheckTypeVO.ALLOW.getCode())
                .info(RuleLogicCheckTypeVO.ALLOW.getInfo())
                .build();

    }
}

3.权重过滤规则的实现类

java 复制代码
/**
 * @author 印东升
 * @description 【抽奖前规则】根据抽奖权重返回可抽奖范围KEY
 * @create 2026-04-10 12:30
 */

@Slf4j
@Component
@LogicStrategy(logicMode = DefaultLogicFactory.LogicModel.RULE_WIGHT)
public class RuleWeightLogicFilter implements ILogicFilter<RuleActionEntity.RaffleBeforeEntity> {

    @Resource
    private IStrategyRepository repository;

    private Long userScore = 4500L;


    /**
     * 权重规则过滤;
     * 1. 权重规则格式;4000:102,103,104,105 5000:102,103,104,105,106,107 6000:102,103,104,105,106,107,108,109
     * 2. 解析数据格式;判断哪个范围符合用户的特定抽奖范围
     *
     * @param ruleMatterEntity 规则物料实体对象
     * @return 规则过滤结果
     */

    @Override
    public RuleActionEntity<RuleActionEntity.RaffleBeforeEntity> filter(RuleMatterEntity ruleMatterEntity) {
        log.info("规则过滤-权重范围 userId:{} strategyId:{} ruleModel:{}", ruleMatterEntity.getUserId(), ruleMatterEntity.getStrategyId(), ruleMatterEntity.getRuleModel());
        //查询表中的规则值,比如4000:102,103,104,105
        String userId = ruleMatterEntity.getUserId();
        Long strategyId = ruleMatterEntity.getStrategyId();
        String ruleValue = repository.queryStrategyRuleValue(ruleMatterEntity.getStrategyId(), ruleMatterEntity.getAwardId(), ruleMatterEntity.getRuleModel());

        // 1. 根据用户ID查询用户抽奖消耗的积分值,本章节我们先写死为固定的值。后续需要从数据库中查询。
        Map<Long, String> analyticalValueGroup = getAnalyticalValue(ruleValue);
        if (null == analyticalValueGroup || analyticalValueGroup.isEmpty()) {
            return RuleActionEntity.<RuleActionEntity.RaffleBeforeEntity>builder()
                    .code(RuleLogicCheckTypeVO.ALLOW.getCode())
                    .info(RuleLogicCheckTypeVO.ALLOW.getInfo())
                    .build();
        }

        // 2. 转换Keys值,并默认排序
        List<Long> analyticalSortedKeys = new ArrayList<>(analyticalValueGroup.keySet());
        Collections.sort(analyticalSortedKeys);

        // 3. 找出最小符合的值,也就是【4500 积分,能找到 4000:102,103,104,105】、【5000 积分,能找到 5000:102,103,104,105,106,107】
        Long nextValue = analyticalSortedKeys.stream()
                .filter(key -> userScore >= key)
                .findFirst()
                .orElse(null);

        if (null != nextValue) {
            return RuleActionEntity.<RuleActionEntity.RaffleBeforeEntity>builder()
                    .data(RuleActionEntity.RaffleBeforeEntity.builder()
                            .strategyId(strategyId)
                            .ruleWeightValueKey(analyticalValueGroup.get(nextValue))
                            .build())
                    .ruleModel(DefaultLogicFactory.LogicModel.RULE_WIGHT.getCode())
                    .code(RuleLogicCheckTypeVO.TAKE_OVER.getCode())
                    .info(RuleLogicCheckTypeVO.TAKE_OVER.getInfo())
                    .build();
        }

        return RuleActionEntity.<RuleActionEntity.RaffleBeforeEntity>builder()
                .code(RuleLogicCheckTypeVO.ALLOW.getCode())
                .info(RuleLogicCheckTypeVO.ALLOW.getInfo())
                .build();

    }

    private Map<Long, String> getAnalyticalValue(String ruleValue) {
        String[] ruleValueGroups = ruleValue.split(Constants.SPACE);
        Map<Long, String> ruleValueMap = new HashMap<>();
        for (String ruleValueKey : ruleValueGroups) {
            // 检查输入是否为空
            if (ruleValueKey == null || ruleValueKey.isEmpty()) {
                return ruleValueMap;
            }
            // 分割字符串以获取键和值
            String[] parts = ruleValueKey.split(Constants.COLON);
            if (parts.length != 2) {
                throw new IllegalArgumentException("rule_weight rule_rule invalid input format" + ruleValueKey);
            }
            ruleValueMap.put(Long.parseLong(parts[0]), ruleValueKey);
        }
        return ruleValueMap;
    }
}

四、测试

注意哈,我们刚刚的大抽象类是实现了IRaffleStrategy接口的哦,所以它的子类也是实现了的,这就是专门为了后续直接调用接口来实现整个规则过滤业务,面向接口编程。这里注入这个接口实际上是注入了子类,也就是那个完整实现了doCheckRaffleBeforeLogic和performRaffle的DefaultRaffleStrategy 。

java 复制代码
public interface IRaffleStrategy {
    RaffleAwardEntity performRaffle(RaffleFactorEntity raffleFactorEntity);
}
java 复制代码
//抽奖策略测试
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest
public class RaffleStrategyTest {

    @Resource
    private IRaffleStrategy raffleStrategy;

    @Resource
    private RuleWeightLogicFilter ruleWeightLogicFilter;

    @Before
    public void setUp() {
        ReflectionTestUtils.setField(ruleWeightLogicFilter, "userScore", 4050L);
    }

    @Test
    public void test_performRaffle() {
        RaffleFactorEntity raffleFactorEntity = RaffleFactorEntity.builder()
                .userId("yds")
                .strategyId(100001L)
                .build();

        RaffleAwardEntity raffleAwardEntity = raffleStrategy.performRaffle(raffleFactorEntity);

        log.info("请求参数:{}", JSON.toJSONString(raffleFactorEntity));
        log.info("测试结果:{}", JSON.toJSONString(raffleAwardEntity));
    }

    @Test
    public void test_performRaffle_blacklist() {
        RaffleFactorEntity raffleFactorEntity = RaffleFactorEntity.builder()
                .userId("user003")  // 黑名单用户 user001,user002,user003
                .strategyId(100001L)
                .build();

        RaffleAwardEntity raffleAwardEntity = raffleStrategy.performRaffle(raffleFactorEntity);

        log.info("请求参数:{}", JSON.toJSONString(raffleFactorEntity));
        log.info("测试结果:{}", JSON.toJSONString(raffleAwardEntity));
    }

}

测试结果如下:

五、总结

这种业务极度复杂的项目真的太难啃了,我觉得必须按照"找核心" 的方式去剖析才能理清楚关系,这一节其实是很开阔眼界的,当然,我认为这里面最厉害的还是那个抽象类,之所以认为它是核心,就是因为它起到了承上启下的作用,上承工厂、接口,下启子类、注解实现类。其实我觉得这一节的具体实现逻辑我后面很快就会忘,但是这样的业务结构确实是大开眼界了。

相关推荐
仍然.2 小时前
多线程---CAS,JUC组件和线程安全的集合类
java·开发语言
不懂的浪漫2 小时前
mqtt-plus 架构解析(五):错误处理与 ErrorAction 聚合策略
java·spring boot·后端·物联网·mqtt·架构
呼啦啦5612 小时前
C++vector
java·c++·缓存
花千树-0102 小时前
MCP + Function Calling:让模型自主驱动工具链完成多步推理
java·agent·react·mcp·toolcall·harness·j-langchain
Benszen2 小时前
Linux容器:轻量级虚拟化革命
java·linux·运维
凸头2 小时前
Lombok 包底层浅析
java
不懂的浪漫2 小时前
mqtt-plus 架构解析(三):Payload 序列化与反序列化,为什么要拆成两条链
java·spring boot·物联网·mqtt·架构
卷福同学2 小时前
去掉手机APP开屏广告,李跳跳2.2下载使用
java·后端·算法
漫霂3 小时前
二叉树的翻转
java·数据结构·算法