2024.7.12 day13
![[抽奖前规则过滤.png]]

情景

上一节完成了当用户抽奖的幸运值(消耗的积分)达到一定值时,分配可以抽奖的范围,需要从strategy实体中找到rule model字段,根据rule_weight在策略规则实体中找到对应的规则并进行解析,在装配策略时将规则装配到缓存中。
这一节需要实现抽奖前置权重和黑名单规则,
![[Pasted image 20240713095331.png]]模板模式,策略模式,工程模式本节使用了

任务

实现用户执行抽奖时判断是否已经超过N积分,如果超过N积分可以在限定范围内进行抽奖,同时如果用户是黑名单用户则只返回固定的奖品。实现涉及到工厂和策略定义出来的规则模型,并满足后续的规则扩展,

设计模式

策略模式

策略模式定义一系列算法,将每个算法封装起来,可以相互替换
抽奖规则过滤使用了策略模式,不同的策略(黑名单规则,权重规则)都是独立的过滤规则,每个规则都实现了ILogicFilter接口,并且根据需要替换或更新规则
代码逻辑通过传入不同的ruleModel选择不同的过滤器执行过滤规则。通过工厂类添加新策略

工厂模式

将对象的创建与使用分离,提供了一种创建对象的接口,具体的创建工作由工厂类负责
DefaultLogicFactory将所有规则逻辑RuleBlackListLogicFilter RuleWeightLogicFilter注册到logicFilterMap中,使用时调用工厂,再选择合适的filter进行规则过滤

模板方法模式

模板方法定义了算法的框架,允许子类重写某些步骤而不改变算法的结构
AbstractRaffleStrategy实现了模板方法模式,定义了抽奖的标准流程,将一些可变部分抽象为子类需要实现的钩子方法,比如规则过滤
performRaffle是模板方法,定义了抽奖的整体流程(参数校验,策略查询,规则过滤,抽奖结果)
doCheckRaffleBeforeLogi是抽象方法,由子类DefaultRaffleStrategy实现,

新增4个实体类

  1. RaffleAwardEntity 抽奖奖品实体。 strategyId, awardId, awardKey, awardConfig, awardDesc. awardKey和awardConfig用于对接发奖策略,为了根据奖品的配置执行不同的发奖逻辑
  2. RaffleFactoryEntity 抽奖因子实体。userId, strategyId. 封装了与用户参与抽奖相关的关键因子,主要用于在抽奖过程中传递用户信息以及所采用的抽奖策略,用来判断用户是否符合某些规则条件
  3. RuleActionEntity 规则动作实体 **泛型 T**:RuleActionEntity 使用泛型 T 来支持不同阶段的规则实体(例如 RaffleBeforeEntityRaffleCenterEntityRaffleAfterEntity
  4. RuleMatterEntity 规则物料实体。 userId, strategyId, awardId, ruleModel, 封装了规则过滤时的必要信息,判断用户是否符合某些规则

行为

1. 在业务service层中创建一个执行抽奖接口

IRaffleStrategy: performRaffle(RaffleFactorEntity)

入参:抽奖因子实体(userID, strategyID)
返回: 抽奖奖品信息实体(strategyID, awardID, awardKey, awardConfig, awardDesc)

  1. 创建接口
  2. 创建抽奖因子和抽奖奖品实体类
  3. 在业务层servcie创建raffle package以及抽象抽奖策略类,用来定义抽奖的标准流程

2. 在service层创建规则package并定义一个抽奖规则逻辑过滤的接口

入参:规则物资(userID, strategyID, awardID, ruleModel)
返回: 规则动作ruleActionEntity

  1. 创建接口
  2. 创建规则物资和规则动作实体对象,注意的是在规则动作中使用泛型,分为抽奖前中后的三个规则动作

3. 定义一个DefalutLogicFactory工厂类和一个@LogicStrategy注解用来管理和实例化一组逻辑过滤器

逻辑过滤器由ILogicFilter实现,根据不同的逻辑模式将它们存储在一个映射中,DefaultLogicFactory就可以用灵活的机制组织访问这些逻辑过滤器

  1. 管理逻辑过滤器实例: 接收所有逻辑过滤器的实现,并存储在一个线程安全的hashmap中
  2. 通过@LogicStrategy注解将每个逻辑过滤器与一个逻辑模式关联起来,根据逻辑模式存储在映射中
  3. 提供openLogicFilter方法用于返回存储的逻辑过滤器映射,使得业务逻辑可以根据需要访问和使用过滤器

4. implement过滤接口ILogicFilter

  1. 黑名单过滤接口 RuleBlackListLogicFilter
    传入规则物料实体,返回规则行为实体
  2. 权重规则过滤接口
    目前数据库中没有用户的积分值
    后续是使用用户的积分值和策略规则中的权重进行比对,并按照权重规则来抽奖

5. 接口都开发好后,开发抽奖流程AbstractRaffleStrategy performRaffle

入参是抽奖因子实体(userId, strategyId)
返回抽奖奖品实体()

IRaffleStrategy

  • IRaffleStrategy 是抽奖策略接口,定义了一个 performRaffle 方法,用于根据传入的 RaffleFactorEntity(包含用户和策略信息)执行抽奖,并返回抽奖奖品的结果。
  • 这个接口定义了抽奖系统的核心操作,后续不同的实现类可以根据具体的策略实现抽奖逻辑。
  • 设计模式:这是标准的策略模式(Strategy Pattern)的应用,通过不同的实现类来定义不同的抽奖逻辑,确保抽奖逻辑可变且灵活。

ILogicFilter

  • ILogicFilter 是抽奖规则过滤接口,定义了规则过滤的行为。每个实现类会根据传入的 RuleMatterEntity 进行规则校验,并返回 RuleActionEntity,其中封装了过滤结果。
  • T 是一个泛型,表示不同阶段的规则实体,比如抽奖前的 RaffleBeforeEntity
  • 设计模式:这是典型的策略模式(Strategy Pattern)的应用,通过不同的 ILogicFilter 实现类来定义不同的规则过滤逻辑,比如黑名单过滤和权重过滤。

DefaultLogicFactory

工厂模式,管理不同的逻辑过滤器ILogicFilter实现类,根据不同业务需求返回相应的过滤器实例

1
2
3
// 定义了一个线程安全的ConcurrentHashMap, 用来存储逻辑模式和响应的ILogicFilter实现(黑名单和权重值)
public Map<String, ILogicFilter<?>> logicFilterMap = new ConcurrentHashMap<>();

@LogicStrategy注解

通过@LogicStrategy注解实现动态注册和解耦逻辑过滤器

**logicMode()**:

  • 注解的 logicMode() 定义了当前逻辑过滤器适用的规则模式,返回值是 DefaultLogicFactory.LogicModel 枚举类型中的一个。每个 LogicModel 代表一种规则类型,比如:
    • RULE_WIGHT:表示权重规则过滤。
    • RULE_BLACKLIST:表示黑名单规则过滤。

4. 流程总结:注解驱动+工厂模式

  1. 注册过滤器

    • 通过注解 @LogicStrategy 为每个过滤器标识其对应的规则类型。
    • 工厂在启动时会扫描所有实现了 ILogicFilter 接口的类,并通过 AnnotationUtils.findAnnotation() 识别类上的注解,将过滤器注册到 logicFilterMap 中。
  2. 调用过滤器

    • 当业务逻辑需要执行规则过滤时,工厂会从 logicFilterMap 中根据规则类型(rule_weightrule_blacklist)提取相应的 ILogicFilter 实现类,并调用其 filter() 方法执行过滤逻辑。
  3. 扩展性

    • 如果未来增加新的规则过滤逻辑,比如 rule_vip,只需定义新的 ILogicFilter 实现类并使用 @LogicStrategy(logicMode = DefaultLogicFactory.LogicModel.RULE_VIP) 注解即可,工厂会自动识别并注册,无需修改现有的工厂逻辑。

AbstractRaffleStrategy

模板模式策略 实现IRaffleStrategy接口,定义了抽奖的标准流程
RaffleAwardEntity performRaffle(RaffleFactorEntity) 定义了抽奖的通用流程步骤,具体的抽奖前规则检查在DefaultRaffleStrategy中的抽象方法doCheckRaffleBeforeLogic实现

  1. 参数校验,根据入参拿到的抽奖因子实体RaffleFactorEntity(userId, strategyId)查看是否有空的现象
  2. 根据strategyId像仓储层查询策略实体strategyEntity(strategyId, strategyDesc, ruleModels), 主要是获取ruleModel, 可能的结果是rule_weight, rule_blacklist
  3. 进行抽奖前规则过滤doCheckRaffleBeforeLogic 进行规则过滤,根据规则过滤结果(返回一个RuleActionEntity),判断是否需要接管
  4. 如果TACK_OVER: RULE_BLACKLIST, 返回固定奖品id. RULE_WIGHT根据权重信息返回进行抽奖
  5. 如果没有TACK_OVER, 进行默认抽奖流程

RuleBlackListLogicFilter

  1. 扫描到LogicStrategy注释,将filter放入hashmap中
  2. 从仓储层根据strategyId, awardId, ruleModle查询strategyRuleValue 100:user001,user002,user003
  3. 将黑名单分组,比对userid是否在黑名单里,如果在,返回TACK_OVER的RuleActionEntity,如果不在返回放行的规则行为实体

RuleWeightLogicFilter

  • 初始化:

    • 类注解 @LogicStrategy 标识该类是权重规则过滤器,通过 DefaultLogicFactory 动态加载到规则过滤机制中。
    • userScore 是用户当前积分,实际应用中通常会从数据库或服务中获取。
  • filter 方法:

    • 负责执行权重规则的过滤,接受一个 RuleMatterEntity(规则物料实体)对象作为输入,包含用户 ID、策略 ID 等信息。
    • 通过 repository.queryStrategyRuleValue() 从数据库查询规则值(例如,”4000:102,103,104”),表示在权重阈值 4000 下可以抽取奖品 102103104

解析权重规则:

  • getSplitWeightValue() 将规则字符串解析成 Map<Long, String>。例如,将 “4000:102,103 5000:105,106” 解析为:

    {4000: "4000:102,103", 5000: "5000:105,106"}

然后筛选出来比userScore小的最大的值就作为key
如果nextValue是存在的,就返回带有ruleWeightValueKey的RuleActionEntity