Lottery项目流程

Lottery项目流程
MT需求设计
抽奖整体流程图
使用脚手架工具创建项目
抽奖策略领域库表设计
- 四张表:策略表、策略奖牌表、奖品表、奖品规则表
基础层持久化数据
- mybatis配置
- infrastructure层dao、po代码开发
策略概率装配
导入redisson
- 在app层加入redisson配置类
- 脚手架已在infrastructure层对redisson的一些方法简单封装
策略装配
- 创建策略领域,策略装配厂,数据查询由repository(infrastructure层)实现
- 创建StrategyRepository
- 通过策略id从redis获取策略奖品list,null则查询数据库并存入redis,具体数据库查询由dao实现
- 回到策略装配厂,通过策略奖品list生产抽奖hashmap并存入redis,供抽奖使用
抽奖的实现思想为以空间换时间。例:一个有100个格子的map,在其中3个格子中都填入奖品一,则对应奖品一的概率为3%,10个格子中都填入奖品二,则对应奖品二的概率为10%,
疑问:为什么要用linkedhashmap来存入奖品,直接用arraylist不好吗,而且这个具体实现方法也有问题,总是向上取整,不得溢出?
具体实现
1、获取最小概率值,概率值总和
2、最小概率值 / 概率值总和(向上取整)得到占位格总数量(尽量节省空间)
3、通过 当前奖品概率 * 占位格总数量(向上取整)得到当前奖品的占位格数,将所有占位格都放入list中
4、Collections.shuffle()对存储的奖品进行乱序操作
5、将list转为Mapper,其中map的key为1,2,3,4…..作为抽奖时get的参数,并存入redis
策略权重概率装配
整节的理解:调用抽奖接口时,依据传入的第二个参数(重载)制定不同的key,抽取redis中对应的表
抽奖流程
抽奖前置规则过滤
草稿:看看测试的调用顺序
- @Resource注入ruleWeightLogicFilter、raffleStrategy
- @Before通过反射ruleWeightLogicFilter暂时修改角色的累计积分,测试累计积分解锁新奖品的功能
- 构建一个抽奖因子实体raffleFactorEntity(用户id、抽奖策略)
- raffleStrategy.performRaffle(raffleFactorEntity)执行抽奖
抽奖嘛,需要抽奖参数,我们就使用一个抽奖因子实体类来表示,最后作为抽奖的参数
具体抽奖方法的结构
- IRaffleStrategy抽奖策略接口
- AbstractRaffleStrategy implements IRaffleStrategy 抽奖策略抽象类,定义抽奖的标准流程
- DefaultRaffleStrategy extends AbstractRaffleStrategy 默认的抽奖策略实现
- 其中的关系:接口定义了抽奖方法performRaffle,抽象类实现了抽奖的过程,但是没有实现doCheckRaffleBeforeLogic(逻辑检查?)
- DefaultRaffleStrategy 实现doCheckRaffleBeforeLogic
performRaffle(RaffleFactorEntity raffleFactorEntity) 方法详细
参数校验
策略查询
查到策略的规则模型
抽奖前 - 规则过滤
1.调用doCheckRaffleBeforeLogic返回RuleActionEntity(规则动作实体)
拿到的参数有抽奖因子实体(策略id,用户id),规则数组logics
创建一个规则过滤器组(map)
规则过滤器由规则工厂logicFactory(这里使用的是DefaultLogicFactory)产出,工厂设计详细在下
处理具体过程:
先判断是否有黑名单规则
从规则过滤器组(map)获取对应的规则过滤器logicFilter
将抽奖的信息打包成规则物料实体ruleMatterEntity
执行方法logicFilter.filter(ruleMatterEntity),得到规则动作实体RuleActionEntity
如果规则动作实体表示被接管,则直接return
再将黑名单规则排除,过滤其他规则
生成新的规则物料实体,调用logicFilter.filter(ruleMatterEntity)
2.定义enum RuleLogicCheckTypeVO规则过滤校验类型值对象
3.ruleActionEntity.getCode与RuleLogicCheckTypeVO比较
4.判断是黑名单规则还是权重规则,进行抽奖
判断的具体过程:
先判断是否受规则影响,受影响则进一步判断
再优先判断是否为黑名单,黑名单则直接返回T中的奖品
是否有权重规则,从T中获取具体权重规则,调用之前写好的抽奖方法
默认抽奖流程
RuleActionEntity(规则动作实体)
1 | private String code = RuleLogicCheckTypeVO.ALLOW.getCode(); |
成员变量code为是否受规则影响
其中包含三个静态内部类,包括抽奖的前中后,也就对应成员变量T
规则工厂DefaultLogicFactory
收集规则生产一个规则过滤器logicFilterMap
通过构造方法将规则装入logicFilterMap
ILogicFilter(规则过滤器)
ILogicFilter
目前有两个实现类RuleBackListLogicFilter和RuleWeightLogicFilter
主要方法filter(RuleMatterEntity ruleMatterEntity)
具体的两个filter实现很简单,围绕RuleActionEntity处理
抽奖中置规则过滤
责任链模式处理抽奖规则
抽奖规则树模型结构设计
流程设计
这里有一个矛盾点需要解决。对于抽奖策略的前置规则过滤是顺序一条链的,有一个成功就可以返回。比如;黑名单抽奖、权重人群抽奖、默认抽奖,总之它只能有一种情况,所以这样的流程是适合责任链的。
那么对于抽奖中到抽奖后的规则,它是一个非多分支情况的规则过滤。单独的责任链是不能满足的,如果是拆分开抽奖中规则和抽奖后规则分阶段处理,中间单独写逻辑处理库存操作。那么是可以实现的。但这样的方式始终不够优雅,配置化的内容较低,后续的规则开发仍需要在代码上改造。所以这里实现一版组合模式的决策树模型设计。
@Resource与@Autowired的区别
@Resource 注解和 @Autowired 注解都是在 Spring Framework 中进行依赖注入的注解,但它们之间有一些区别:
- 来源不同:
@Resource 注解是由 Java EE 提供的注解,它定义在 javax.annotation.Resource 包下。
@Autowired 注解是由 Spring 提供的注解,它定义在 org.springframework.beans.factory.annotation.Autowired 包下。
- 依赖注入策略不同:
@Resource 注解默认按照名称(你写的变量名)进行依赖注入,名称匹配失败则按类型来匹配,如果有多个具有相同类型的依赖,可以使用 name 属性指定依赖的名称。
@Autowired 注解默认按照类型进行依赖注入,如果有多个具有相同类型的依赖,可以使用 @Qualifier 注解或者 @Primary 注解指定依赖的名称或主要依赖。
- 兼容性不同:
@Resource 注解属于 Java EE 标准的注解,在 Java EE 环境中可以正常使用。
@Autowired 注解是 Spring Framework 提供的特定于 Spring 的注解,它可以在 Spring 环境下使用。
- 注解属性不同:
@Resource 注解可以添加 name 属性来指定依赖的名称,还可以添加 mappedName 属性来指定依赖的 JNDI 名称。
@Autowired 注解可以添加 required 属性来指定依赖是否必须,默认为 true。
综上所述,@Resource 注解和 @Autowired 注解在功能上有一些相似之处,但也有一些区别。如果你在 Java EE 环境中使用依赖注入,可以选择使用 @Resource 注解;如果在 Spring Framework 环境中使用依赖注入,可以选择使用 @Autowired 注解。需要根据具体的情况选择适合的注解进行依赖注入。
泛型的相关
- 在Java中,泛型需要在定义类、方法或接口时声明。声明泛型的语法是在类名或方法名后面使用尖括号<>,并在尖括号中指定泛型参数的类型
<?>
通配符泛型
它表示未知类型,可以用来表示任意类型。通配符泛型通常用于方法参数的声明或变量的定义,可以接受任意类型的参数例如:
1
2
3
4
5
6
7
8
9
10
11 javapublic void printList(List<?> list) {
for (Object item : list) {
System.out.println(item);
}
}
List<String> stringList = new ArrayList<>();
stringList.add("Hello");
stringList.add("World");
printList(stringList);需要注意的是,使用
<?>
通配符泛型时,只能对元素进行读取操作,不能对元素进行写入操作。这是因为Java无法确定通配符泛型的具体类型,所以无法保证类型安全Java不允许直接进行泛型类型的转换,所以我们需要先转换为通配符类型,再转换为目标类型,在进行这种类型转换时,需要确保类型转换是安全的。
- 泛型的擦除
在Java中,泛型信息只存在于代码编译阶段,在编译后会被擦除,转换为原始类型。因此,在运行时无法获取泛型类型的具体信息