From b647fac1fbac6e6f18b9f2abf727a51727836895 Mon Sep 17 00:00:00 2001 From: yongfeng <2283865573@qq.com> Date: Sun, 17 Nov 2024 17:53:43 +0800 Subject: [PATCH] =?UTF-8?q?=E7=AD=96=E7=95=A5=E6=9D=83=E9=87=8D=E6=A6=82?= =?UTF-8?q?=E7=8E=87=E8=A3=85=E9=85=8D=E3=80=81=E6=8A=BD=E5=A5=96=E5=89=8D?= =?UTF-8?q?=E7=BD=AE=E8=A7=84=E5=88=99=E8=BF=87=E6=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/dataSources.xml | 14 +++ .../mybatis/mapper/strategy_mapper.xml | 8 +- .../mybatis/mapper/strategy_rule_mapper.xml | 15 ++- .../test/domain/RaffleStrategyTest.java | 65 ++++++++++ ...t.java => StrategyArmoryDispatchTest.java} | 10 +- .../cn/bugstack/test/domain/StrategyTest.java | 110 +++++++++++++++++ .../model/entity/RaffleAwardEntity.java | 30 +++++ .../model/entity/RaffleFactorEntity.java | 25 ++++ .../model/entity/RuleActionEntity.java | 61 ++++++++++ .../model/entity/RuleMatterEntity.java | 30 +++++ .../strategy/model/entity/StrategyEntity.java | 41 +++++++ .../model/entity/StrategyRuleEntity.java | 69 +++++++++++ .../model/valobj/RuleLogicCheckTypeVO.java | 24 ++++ .../repository/IStrategyRepository.java | 16 ++- .../strategy/service/IRaffleStrategy.java | 18 +++ .../service/annotation/LogicStrategy.java | 23 ++++ .../service/armory/IStrategyArmory.java | 7 -- .../service/armory/IStrategyDispatch.java | 14 +++ .../service/armory/StrategyArmory.java | 77 ------------ .../armory/StrategyArmoryDispatch.java | 114 ++++++++++++++++++ .../raffle/AbstractRaffleStrategy.java | 78 ++++++++++++ .../service/raffle/DefaultRaffleStrategy.java | 84 +++++++++++++ .../strategy/service/rule/ILogicFilter.java | 17 +++ .../rule/factory/DefaultLogicFactory.java | 52 ++++++++ .../rule/impl/RuleBackListLogicFilter.java | 63 ++++++++++ .../rule/impl/RuleWeightLogicFilter.java | 105 ++++++++++++++++ .../persistent/dao/IStrategyDao.java | 7 +- .../persistent/dao/IStrategyRuleDao.java | 8 +- .../infrastructure/persistent/po/Award.java | 2 + .../persistent/po/Strategy.java | 19 +-- .../persistent/po/StrategyAward.java | 2 + .../persistent/po/StrategyRule.java | 1 + .../repository/StrategyRepository.java | 70 +++++++++-- .../cn/bugstack/types/common/Constants.java | 5 + .../cn/bugstack/types/enums/ResponseCode.java | 2 + 35 files changed, 1175 insertions(+), 111 deletions(-) create mode 100644 xfg-frame-archetype-lite-app/src/test/java/cn/bugstack/test/domain/RaffleStrategyTest.java rename xfg-frame-archetype-lite-app/src/test/java/cn/bugstack/test/domain/{StrategyArmoryTest.java => StrategyArmoryDispatchTest.java} (93%) create mode 100644 xfg-frame-archetype-lite-app/src/test/java/cn/bugstack/test/domain/StrategyTest.java create mode 100644 xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/model/entity/RaffleAwardEntity.java create mode 100644 xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/model/entity/RaffleFactorEntity.java create mode 100644 xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/model/entity/RuleActionEntity.java create mode 100644 xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/model/entity/RuleMatterEntity.java create mode 100644 xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/model/entity/StrategyEntity.java create mode 100644 xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/model/entity/StrategyRuleEntity.java create mode 100644 xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/model/valobj/RuleLogicCheckTypeVO.java create mode 100644 xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/service/IRaffleStrategy.java create mode 100644 xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/service/annotation/LogicStrategy.java create mode 100644 xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/service/armory/IStrategyDispatch.java delete mode 100644 xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/service/armory/StrategyArmory.java create mode 100644 xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/service/armory/StrategyArmoryDispatch.java create mode 100644 xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/service/raffle/AbstractRaffleStrategy.java create mode 100644 xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/service/raffle/DefaultRaffleStrategy.java create mode 100644 xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/service/rule/ILogicFilter.java create mode 100644 xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/service/rule/factory/DefaultLogicFactory.java create mode 100644 xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/service/rule/impl/RuleBackListLogicFilter.java create mode 100644 xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/service/rule/impl/RuleWeightLogicFilter.java diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml index 939b864..b64454b 100644 --- a/.idea/dataSources.xml +++ b/.idea/dataSources.xml @@ -13,5 +13,19 @@ $ProjectFileDir$ + + mysql.8 + true + true + $PROJECT_DIR$/xfg-frame-archetype-lite-app/src/main/resources/application-dev.yml + com.mysql.cj.jdbc.Driver + jdbc:mysql://127.0.0.1:13306/big_market?useUnicode=true&characterEncoding=utf8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&serverTimezone=UTC&useSSL=true + + + + + + $ProjectFileDir$ + \ No newline at end of file diff --git a/xfg-frame-archetype-lite-app/src/main/resources/mybatis/mapper/strategy_mapper.xml b/xfg-frame-archetype-lite-app/src/main/resources/mybatis/mapper/strategy_mapper.xml index 03af8bd..e206839 100644 --- a/xfg-frame-archetype-lite-app/src/main/resources/mybatis/mapper/strategy_mapper.xml +++ b/xfg-frame-archetype-lite-app/src/main/resources/mybatis/mapper/strategy_mapper.xml @@ -6,6 +6,7 @@ + @@ -16,5 +17,10 @@ limit 10 - + + diff --git a/xfg-frame-archetype-lite-app/src/main/resources/mybatis/mapper/strategy_rule_mapper.xml b/xfg-frame-archetype-lite-app/src/main/resources/mybatis/mapper/strategy_rule_mapper.xml index fe61a80..326e8de 100644 --- a/xfg-frame-archetype-lite-app/src/main/resources/mybatis/mapper/strategy_rule_mapper.xml +++ b/xfg-frame-archetype-lite-app/src/main/resources/mybatis/mapper/strategy_rule_mapper.xml @@ -20,5 +20,18 @@ limit 10 - + + + + diff --git a/xfg-frame-archetype-lite-app/src/test/java/cn/bugstack/test/domain/RaffleStrategyTest.java b/xfg-frame-archetype-lite-app/src/test/java/cn/bugstack/test/domain/RaffleStrategyTest.java new file mode 100644 index 0000000..0dd43dc --- /dev/null +++ b/xfg-frame-archetype-lite-app/src/test/java/cn/bugstack/test/domain/RaffleStrategyTest.java @@ -0,0 +1,65 @@ +package cn.bugstack.test.domain; + +import cn.bugstack.domain.strategy.model.entity.RaffleAwardEntity; +import cn.bugstack.domain.strategy.model.entity.RaffleFactorEntity; +import cn.bugstack.domain.strategy.service.IRaffleStrategy; +import cn.bugstack.domain.strategy.service.rule.impl.RuleWeightLogicFilter; +import com.alibaba.fastjson.JSON; +import lombok.extern.slf4j.Slf4j; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import javax.annotation.Resource; +import java.security.PrivateKey; + +/** + * @ClassName: RaffleStrategyTest + * @Description: + * @Author: zhaoyongfeng + * @Date: 2024/11/17 17:11 + */ +@SpringBootTest +@Slf4j +@RunWith(SpringRunner.class) +public class RaffleStrategyTest { + @Resource + private IRaffleStrategy raffleStrategy; + @Resource + private RuleWeightLogicFilter ruleWeightLogicFilter; + @Before + public void setUp() { + ReflectionTestUtils.setField(ruleWeightLogicFilter, "userScore", 4500L); + } + @Test + public void test_performRaffle() { + RaffleFactorEntity raffleFactorEntity = RaffleFactorEntity.builder() + .userId("xiaofuge") + .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)); + } + + + +} diff --git a/xfg-frame-archetype-lite-app/src/test/java/cn/bugstack/test/domain/StrategyArmoryTest.java b/xfg-frame-archetype-lite-app/src/test/java/cn/bugstack/test/domain/StrategyArmoryDispatchTest.java similarity index 93% rename from xfg-frame-archetype-lite-app/src/test/java/cn/bugstack/test/domain/StrategyArmoryTest.java rename to xfg-frame-archetype-lite-app/src/test/java/cn/bugstack/test/domain/StrategyArmoryDispatchTest.java index ae682c4..5a1ee06 100644 --- a/xfg-frame-archetype-lite-app/src/test/java/cn/bugstack/test/domain/StrategyArmoryTest.java +++ b/xfg-frame-archetype-lite-app/src/test/java/cn/bugstack/test/domain/StrategyArmoryDispatchTest.java @@ -18,18 +18,18 @@ import javax.annotation.Resource; @SpringBootTest @Slf4j @RunWith(SpringRunner.class) -public class StrategyArmoryTest { +public class StrategyArmoryDispatchTest { @Resource private IStrategyArmory strategyArmory; - @Test + /*@Test public void test_strategyArmory(){ strategyArmory.assembleLotteryStrategy(100002L); } - /** + *//** * 从装配的策略中随机获取奖品ID值 - */ + *//* @Test public void test_getAssembleRandomVal() { log.info("测试结果:{} - 奖品ID值", strategyArmory.getRandomAwardId(100002L)); @@ -38,5 +38,5 @@ public class StrategyArmoryTest { log.info("测试结果:{} - 奖品ID值", strategyArmory.getRandomAwardId(100002L)); log.info("测试结果:{} - 奖品ID值", strategyArmory.getRandomAwardId(100002L)); - } + }*/ } diff --git a/xfg-frame-archetype-lite-app/src/test/java/cn/bugstack/test/domain/StrategyTest.java b/xfg-frame-archetype-lite-app/src/test/java/cn/bugstack/test/domain/StrategyTest.java new file mode 100644 index 0000000..b4cdd87 --- /dev/null +++ b/xfg-frame-archetype-lite-app/src/test/java/cn/bugstack/test/domain/StrategyTest.java @@ -0,0 +1,110 @@ +package cn.bugstack.test.domain; + +import cn.bugstack.domain.strategy.service.armory.IStrategyArmory; +import cn.bugstack.domain.strategy.service.armory.IStrategyDispatch; +import cn.bugstack.infrastructure.persistent.redis.IRedisService; +import lombok.extern.slf4j.Slf4j; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.redisson.api.RMap; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +import javax.annotation.Resource; +import java.util.*; + +/** + * @ClassName: StrategyTest + * @Description: + * @Author: zhaoyongfeng + * @Date: 2024/11/17 17:18 + */ +@Slf4j +@RunWith(SpringRunner.class) +@SpringBootTest +public class StrategyTest { + + @Resource + private IStrategyArmory strategyArmory; + + @Resource + private IStrategyDispatch strategyDispatch; + + /** + * 策略ID;100001L、100002L 装配的时候创建策略表写入到 Redis Map 中 + */ + @Before + public void test_strategyArmory() { + boolean success = strategyArmory.assembleLotteryStrategy(100001L); + log.info("测试结果:{}", success); + } + + /** + * 从装配的策略中随机获取奖品ID值 + */ + @Test + public void test_getRandomAwardId() { + log.info("测试结果:{} - 奖品ID值", strategyDispatch.getRandomAwardId(100001L)); + } + + /** + * 根据策略ID+权重值,从装配的策略中随机获取奖品ID值 + */ + @Test + public void test_getRandomAwardId_ruleWeightValue() { + log.info("测试结果:{} - 4000 策略配置", strategyDispatch.getRandomAwardId(100001L, "4000:102,103,104,105")); + log.info("测试结果:{} - 5000 策略配置", strategyDispatch.getRandomAwardId(100001L, "5000:102,103,104,105,106,107")); + log.info("测试结果:{} - 6000 策略配置", strategyDispatch.getRandomAwardId(100001L, "6000:102,103,104,105,106,107,108,109")); + } + + @Resource + private IRedisService redisService; + + @Test + public void test_map() { + RMap map = redisService.getMap("strategy_id_100001"); + map.put(1, 101); + map.put(2, 101); + map.put(3, 101); + map.put(4, 102); + map.put(5, 102); + map.put(6, 102); + map.put(7, 103); + map.put(8, 103); + map.put(9, 104); + map.put(10, 105); + + log.info("测试结果:{}", redisService.getMap("strategy_id_100001").get(1)); + } + + @Test + public void test_shuffle(){ + Map strategyAwardSearchRateTable = new HashMap<>(); + // 添加内容到Map中 + strategyAwardSearchRateTable.put(1, 10); + strategyAwardSearchRateTable.put(2, 20); + strategyAwardSearchRateTable.put(3, 30); + strategyAwardSearchRateTable.put(4, 40); + + // 将Map中的值转换为List + List valueList = new ArrayList<>(strategyAwardSearchRateTable.values()); + + // 使用Collections.shuffle()方法对值的List进行乱序 + Collections.shuffle(valueList); + + // 将乱序后的值重新放回Map中 + Map randomizedMap = new LinkedHashMap<>(); + Iterator valueIterator = valueList.iterator(); + for (Integer key : strategyAwardSearchRateTable.keySet()) { + randomizedMap.put(key, valueIterator.next()); + } + + // 打印乱序后的Map内容 + for (Map.Entry entry : randomizedMap.entrySet()) { + System.out.println("Key: " + entry.getKey() + ", Value: " + entry.getValue()); + } + } + +} + diff --git a/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/model/entity/RaffleAwardEntity.java b/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/model/entity/RaffleAwardEntity.java new file mode 100644 index 0000000..324474a --- /dev/null +++ b/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/model/entity/RaffleAwardEntity.java @@ -0,0 +1,30 @@ +package cn.bugstack.domain.strategy.model.entity; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @ClassName: RaffleAwardEntity + * @Description: 抽奖奖品实体 + * @Author: zhaoyongfeng + * @Date: 2024/11/17 16:25 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class RaffleAwardEntity { + /** 策略ID */ + private Long strategyId; + /** 奖品ID */ + private Integer awardId; + /** 奖品对接标识 - 每一个都是一个对应的发奖策略 */ + private String awardKey; + /** 奖品配置信息 */ + private String awardConfig; + /** 奖品内容描述 */ + private String awardDesc; + +} diff --git a/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/model/entity/RaffleFactorEntity.java b/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/model/entity/RaffleFactorEntity.java new file mode 100644 index 0000000..4aac430 --- /dev/null +++ b/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/model/entity/RaffleFactorEntity.java @@ -0,0 +1,25 @@ +package cn.bugstack.domain.strategy.model.entity; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.checkerframework.checker.units.qual.A; + +/** + * @ClassName: RaffleFactorEntity + * @Description: 抽奖因子实体 + * @Author: zhaoyongfeng + * @Date: 2024/11/17 16:26 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class RaffleFactorEntity { + /** 用户ID */ + private String userId; + /** 策略ID */ + private Long strategyId; + +} diff --git a/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/model/entity/RuleActionEntity.java b/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/model/entity/RuleActionEntity.java new file mode 100644 index 0000000..8e40e95 --- /dev/null +++ b/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/model/entity/RuleActionEntity.java @@ -0,0 +1,61 @@ +package cn.bugstack.domain.strategy.model.entity; + +import cn.bugstack.domain.strategy.model.valobj.RuleLogicCheckTypeVO; +import lombok.*; + +/** + * @ClassName: RuleActionEntity + * @Description: 规则动作 + * @Author: zhaoyongfeng + * @Date: 2024/11/17 16:26 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class RuleActionEntity { + + 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 { + + } + +} + diff --git a/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/model/entity/RuleMatterEntity.java b/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/model/entity/RuleMatterEntity.java new file mode 100644 index 0000000..365a153 --- /dev/null +++ b/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/model/entity/RuleMatterEntity.java @@ -0,0 +1,30 @@ +package cn.bugstack.domain.strategy.model.entity; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.checkerframework.checker.units.qual.A; + +/** + * @ClassName: RuleMatterEntity + * @Description: 规则物料实体对象,用于过滤规则的必要参数信息 + * @Author: zhaoyongfeng + * @Date: 2024/11/17 16:26 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class RuleMatterEntity { + + /** 用户ID */ + private String userId; + /** 策略ID */ + private Long strategyId; + /** 抽奖奖品ID【规则类型为策略,则不需要奖品ID】 */ + private Integer awardId; + /** 抽奖规则类型【rule_random - 随机值计算、rule_lock - 抽奖几次后解锁、rule_luck_award - 幸运奖(兜底奖品)】 */ + private String ruleModel; + +} diff --git a/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/model/entity/StrategyEntity.java b/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/model/entity/StrategyEntity.java new file mode 100644 index 0000000..6161509 --- /dev/null +++ b/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/model/entity/StrategyEntity.java @@ -0,0 +1,41 @@ +package cn.bugstack.domain.strategy.model.entity; + +import cn.bugstack.types.common.Constants; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.apache.commons.lang3.StringUtils; + +/** + * @ClassName: StrategyEntity + * @Description: 策略实体 + * @Author: zhaoyongfeng + * @Date: 2024/11/15 23:35 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class StrategyEntity { + + /** 抽奖策略ID */ + private Long strategyId; + /** 抽奖策略描述 */ + private String strategyDesc; + /** 抽奖规则模型 rule_weight,rule_blacklist */ + private String ruleModels; + + public String[] ruleModels() { + if (StringUtils.isBlank(ruleModels)) return null; + return ruleModels.split(Constants.SPLIT); + } + + public String getRuleWeight() { + String[] ruleModels = this.ruleModels(); + for (String ruleModel : ruleModels) { + if ("rule_weight".equals(ruleModel)) return ruleModel; + } + return null; + } +} diff --git a/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/model/entity/StrategyRuleEntity.java b/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/model/entity/StrategyRuleEntity.java new file mode 100644 index 0000000..3c4cd8a --- /dev/null +++ b/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/model/entity/StrategyRuleEntity.java @@ -0,0 +1,69 @@ +package cn.bugstack.domain.strategy.model.entity; + +import cn.bugstack.types.common.Constants; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @ClassName: StrategyRuleEntity + * @Description: 策略规则实体 + * @Author: zhaoyongfeng + * @Date: 2024/11/15 23:36 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class StrategyRuleEntity { + + /** 抽奖策略ID */ + private Long strategyId; + /** 抽奖奖品ID【规则类型为策略,则不需要奖品ID】 */ + private Integer awardId; + /** 抽象规则类型;1-策略规则、2-奖品规则 */ + private Integer ruleType; + /** 抽奖规则类型【rule_random - 随机值计算、rule_lock - 抽奖几次后解锁、rule_luck_award - 幸运奖(兜底奖品)】 */ + private String ruleModel; + /** 抽奖规则比值 */ + private String ruleValue; + /** 抽奖规则描述 */ + private String ruleDesc; + + /** + * 获取权重值 + * 数据案例;4000:102,103,104,105 5000:102,103,104,105,106,107 6000:102,103,104,105,106,107,108,109 + */ + public Map> getRuleWeightValues() { + if (!"rule_weight".equals(ruleModel)) return null; + String[] ruleValueGroups = ruleValue.split(Constants.SPACE); + Map> resultMap = new HashMap<>(); + for (String ruleValueGroup : ruleValueGroups) { + // 检查输入是否为空 + if (ruleValueGroup == null || ruleValueGroup.isEmpty()) { + return resultMap; + } + // 分割字符串以获取键和值 + String[] parts = ruleValueGroup.split(Constants.COLON); + if (parts.length != 2) { + throw new IllegalArgumentException("rule_weight rule_rule invalid input format" + ruleValueGroup); + } + // 解析值 + String[] valueStrings = parts[1].split(Constants.SPLIT); + List values = new ArrayList<>(); + for (String valueString : valueStrings) { + values.add(Integer.parseInt(valueString)); + } + // 将键和值放入Map中 + resultMap.put(ruleValueGroup, values); + } + + return resultMap; + } +} diff --git a/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/model/valobj/RuleLogicCheckTypeVO.java b/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/model/valobj/RuleLogicCheckTypeVO.java new file mode 100644 index 0000000..7ec49c0 --- /dev/null +++ b/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/model/valobj/RuleLogicCheckTypeVO.java @@ -0,0 +1,24 @@ +package cn.bugstack.domain.strategy.model.valobj; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * @ClassName: RuleLogicCheckTypeVO + * @Description: 规则过滤校验类型值对象 + * @Author: zhaoyongfeng + * @Date: 2024/11/17 16:24 + */ +@Getter +@AllArgsConstructor +public enum RuleLogicCheckTypeVO { + + ALLOW("0000", "放行;执行后续的流程,不受规则引擎影响"), + TAKE_OVER("0001","接管;后续的流程,受规则引擎执行结果影响"), + ; + + private final String code; + private final String info; + +} + diff --git a/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/repository/IStrategyRepository.java b/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/repository/IStrategyRepository.java index 1b19192..8517af9 100644 --- a/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/repository/IStrategyRepository.java +++ b/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/repository/IStrategyRepository.java @@ -1,17 +1,29 @@ package cn.bugstack.domain.strategy.repository; import cn.bugstack.domain.strategy.model.entity.StrategyAwardEntity; +import cn.bugstack.domain.strategy.model.entity.StrategyEntity; +import cn.bugstack.domain.strategy.model.entity.StrategyRuleEntity; import java.util.List; import java.util.Map; public interface IStrategyRepository { + List queryStrategyAwardList(Long strategyId); - void storeStrategyAwardSearchRateTable(Long strategyId, Integer rateRange, Map strategyAwardSearchRateTable); + void storeStrategyAwardSearchRateTable(String key, Integer rateRange, Map strategyAwardSearchRateTable); - Integer getStrategyAwardAssemble(Long strategyId, Integer rateKey); + Integer getStrategyAwardAssemble(String key, Integer rateKey); int getRateRange(Long strategyId); + int getRateRange(String key); + + StrategyEntity queryStrategyEntityByStrategyId(Long strategyId); + + StrategyRuleEntity queryStrategyRule(Long strategyId, String ruleModel); + + String queryStrategyRuleValue(Long strategyId, Integer awardId, String ruleModel); + + } diff --git a/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/service/IRaffleStrategy.java b/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/service/IRaffleStrategy.java new file mode 100644 index 0000000..ff78c44 --- /dev/null +++ b/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/service/IRaffleStrategy.java @@ -0,0 +1,18 @@ +package cn.bugstack.domain.strategy.service; + + +import cn.bugstack.domain.strategy.model.entity.RaffleAwardEntity; +import cn.bugstack.domain.strategy.model.entity.RaffleFactorEntity; + +public interface IRaffleStrategy { + + /** + * 执行抽奖;用抽奖因子入参,执行抽奖计算,返回奖品信息 + * + * @param raffleFactorEntity 抽奖因子实体对象,根据入参信息计算抽奖结果 + * @return 抽奖的奖品 + */ + RaffleAwardEntity performRaffle(RaffleFactorEntity raffleFactorEntity); + +} + diff --git a/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/service/annotation/LogicStrategy.java b/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/service/annotation/LogicStrategy.java new file mode 100644 index 0000000..efb233c --- /dev/null +++ b/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/service/annotation/LogicStrategy.java @@ -0,0 +1,23 @@ +package cn.bugstack.domain.strategy.service.annotation; + +import cn.bugstack.domain.strategy.service.rule.factory.DefaultLogicFactory; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @ClassName: LogicStrategy + * @Description: 策略自定义枚举 + * @Author: zhaoyongfeng + * @Date: 2024/11/17 16:02 + */ +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface LogicStrategy { + + DefaultLogicFactory.LogicModel logicMode(); + +} + diff --git a/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/service/armory/IStrategyArmory.java b/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/service/armory/IStrategyArmory.java index 18859a5..ff670a5 100644 --- a/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/service/armory/IStrategyArmory.java +++ b/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/service/armory/IStrategyArmory.java @@ -10,12 +10,5 @@ public interface IStrategyArmory { */ boolean assembleLotteryStrategy(Long strategyId); - /** - * 获取抽奖策略装配的随机结果 - * - * @param strategyId 策略ID - * @return 抽奖结果 - */ - Integer getRandomAwardId(Long strategyId); } diff --git a/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/service/armory/IStrategyDispatch.java b/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/service/armory/IStrategyDispatch.java new file mode 100644 index 0000000..cafea34 --- /dev/null +++ b/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/service/armory/IStrategyDispatch.java @@ -0,0 +1,14 @@ +package cn.bugstack.domain.strategy.service.armory; + +public interface IStrategyDispatch { + /** + * 获取抽奖策略装配的随机结果 + * + * @param strategyId 策略ID + * @return 抽奖结果 + */ + Integer getRandomAwardId(Long strategyId); + + Integer getRandomAwardId(Long strategyId, String ruleWeightValue); + +} diff --git a/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/service/armory/StrategyArmory.java b/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/service/armory/StrategyArmory.java deleted file mode 100644 index 6d91d70..0000000 --- a/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/service/armory/StrategyArmory.java +++ /dev/null @@ -1,77 +0,0 @@ -package cn.bugstack.domain.strategy.service.armory; - -import cn.bugstack.domain.strategy.model.entity.StrategyAwardEntity; -import cn.bugstack.domain.strategy.repository.IStrategyRepository; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; - -import javax.annotation.Resource; -import java.math.BigDecimal; -import java.math.RoundingMode; -import java.security.SecureRandom; -import java.util.*; - -/** - * @ClassName: StrategyArmory - * @Description: 策略装配库(兵工厂),负责初始化策略计算 - * @Author: zhaoyongfeng - * @Date: 2024/11/14 17:39 - */ -@Slf4j -@Service -public class StrategyArmory implements IStrategyArmory{ - @Resource - private IStrategyRepository repository; - - @Override - public boolean assembleLotteryStrategy(Long strategyId) { - //1. 查询策略配置 - List strategyAwardEntities = repository.queryStrategyAwardList(strategyId); - // 获取最小概率值 - BigDecimal minAwardRate = strategyAwardEntities.stream() - .map(StrategyAwardEntity::getAwardRate) - .min(BigDecimal::compareTo) - .orElse(BigDecimal.ZERO); - - // 3. 获取概率值总和 - BigDecimal totalAwardRate = strategyAwardEntities.stream() - .map(StrategyAwardEntity::getAwardRate) - .reduce(BigDecimal.ZERO, BigDecimal::add); -//TODO 需要修改问题 - // 4. 用 1 % 0.0001 获得概率范围,百分位、千分位、万分位 - BigDecimal rateRange = totalAwardRate.divide(minAwardRate, 0, RoundingMode.CEILING); - // 5. 生成策略奖品概率查找表「这里指需要在list集合中,存放上对应的奖品占位即可,占位越多等于概率越高」 - - ArrayList strategyAwardSearchRateTables = new ArrayList<>(rateRange.intValue()); - for(StrategyAwardEntity strategyAward: strategyAwardEntities){ - Integer awardId = strategyAward.getAwardId(); - BigDecimal awardRate = strategyAward.getAwardRate(); - // 计算出每个概率值需要存放到查找表的数量,循环填充 - for (int i = 0; i < rateRange.multiply(awardRate).setScale(0, RoundingMode.CEILING).intValue(); i++) { - strategyAwardSearchRateTables.add(awardId); - } - - } - // 6. 对存储的奖品进行乱序操作 - Collections.shuffle(strategyAwardSearchRateTables); - // 7. 生成出Map集合,key值,对应的就是后续的概率值。通过概率来获得对应的奖品ID - Map shuffleStrategyAwardSearchRateTable = new LinkedHashMap<>(); - for (int i = 0; i < strategyAwardSearchRateTables.size(); i++) { - shuffleStrategyAwardSearchRateTable.put(i, strategyAwardSearchRateTables.get(i)); - } - // 8. 存放到 Redis - repository.storeStrategyAwardSearchRateTable(strategyId, shuffleStrategyAwardSearchRateTable.size(), shuffleStrategyAwardSearchRateTable); - - return true; - - } - - @Override - public Integer getRandomAwardId(Long strategyId) { - // 分布式部署下,不一定为当前应用做的策略装配。也就是值不一定会保存到本应用,而是分布式应用,所以需要从 Redis 中获取。 - int rateRange = repository.getRateRange(strategyId); - // 通过生成的随机值,获取概率值奖品查找表的结果 - return repository.getStrategyAwardAssemble(strategyId, new SecureRandom().nextInt(rateRange)); - } - -} diff --git a/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/service/armory/StrategyArmoryDispatch.java b/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/service/armory/StrategyArmoryDispatch.java new file mode 100644 index 0000000..1893b80 --- /dev/null +++ b/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/service/armory/StrategyArmoryDispatch.java @@ -0,0 +1,114 @@ +package cn.bugstack.domain.strategy.service.armory; + +import cn.bugstack.domain.strategy.model.entity.StrategyAwardEntity; +import cn.bugstack.domain.strategy.model.entity.StrategyEntity; +import cn.bugstack.domain.strategy.model.entity.StrategyRuleEntity; +import cn.bugstack.domain.strategy.repository.IStrategyRepository; +import cn.bugstack.types.enums.ResponseCode; +import cn.bugstack.types.exception.AppException; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.security.SecureRandom; +import java.util.*; + +/** + * @ClassName: StrategyArmory + * @Description: 策略装配库(兵工厂),负责初始化策略计算 + * @Author: zhaoyongfeng + * @Date: 2024/11/14 17:39 + */ +@Slf4j +@Service +public class StrategyArmoryDispatch implements IStrategyArmory,IStrategyDispatch{ + @Resource + private IStrategyRepository repository; + + @Override + public boolean assembleLotteryStrategy(Long strategyId) { + // 1. 查询策略配置 + List strategyAwardEntities = repository.queryStrategyAwardList(strategyId); + assembleLotteryStrategy(String.valueOf(strategyId), strategyAwardEntities); + + // 2. 权重策略配置 - 适用于 rule_weight 权重规则配置 + StrategyEntity strategyEntity = repository.queryStrategyEntityByStrategyId(strategyId); + String ruleWeight = strategyEntity.getRuleWeight(); + if (null == ruleWeight) return true; + + StrategyRuleEntity strategyRuleEntity = repository.queryStrategyRule(strategyId, ruleWeight); + if (null == strategyRuleEntity) { + throw new AppException(ResponseCode.STRATEGY_RULE_WEIGHT_IS_NULL.getCode(), ResponseCode.STRATEGY_RULE_WEIGHT_IS_NULL.getInfo()); + } + Map> ruleWeightValueMap = strategyRuleEntity.getRuleWeightValues(); + Set keys = ruleWeightValueMap.keySet(); + for (String key : keys) { + List ruleWeightValues = ruleWeightValueMap.get(key); + ArrayList strategyAwardEntitiesClone = new ArrayList<>(strategyAwardEntities); + strategyAwardEntitiesClone.removeIf(entity -> !ruleWeightValues.contains(entity.getAwardId())); + assembleLotteryStrategy(String.valueOf(strategyId).concat("_").concat(key), strategyAwardEntitiesClone); + } + + return true; + } + + private void assembleLotteryStrategy(String key, List strategyAwardEntities) { + // 1. 获取最小概率值 + BigDecimal minAwardRate = strategyAwardEntities.stream() + .map(StrategyAwardEntity::getAwardRate) + .min(BigDecimal::compareTo) + .orElse(BigDecimal.ZERO); + + // 2. 获取概率值总和 + BigDecimal totalAwardRate = strategyAwardEntities.stream() + .map(StrategyAwardEntity::getAwardRate) + .reduce(BigDecimal.ZERO, BigDecimal::add); + + // 3. 用 1 % 0.0001 获得概率范围,百分位、千分位、万分位 + BigDecimal rateRange = totalAwardRate.divide(minAwardRate, 0, RoundingMode.CEILING); + + // 4. 生成策略奖品概率查找表「这里指需要在list集合中,存放上对应的奖品占位即可,占位越多等于概率越高」 + List strategyAwardSearchRateTables = new ArrayList<>(rateRange.intValue()); + for (StrategyAwardEntity strategyAward : strategyAwardEntities) { + Integer awardId = strategyAward.getAwardId(); + BigDecimal awardRate = strategyAward.getAwardRate(); + // 计算出每个概率值需要存放到查找表的数量,循环填充 + for (int i = 0; i < rateRange.multiply(awardRate).setScale(0, RoundingMode.CEILING).intValue(); i++) { + strategyAwardSearchRateTables.add(awardId); + } + } + + // 5. 对存储的奖品进行乱序操作 + Collections.shuffle(strategyAwardSearchRateTables); + + // 6. 生成出Map集合,key值,对应的就是后续的概率值。通过概率来获得对应的奖品ID + Map shuffleStrategyAwardSearchRateTable = new LinkedHashMap<>(); + for (int i = 0; i < strategyAwardSearchRateTables.size(); i++) { + shuffleStrategyAwardSearchRateTable.put(i, strategyAwardSearchRateTables.get(i)); + } + + // 7. 存放到 Redis + repository.storeStrategyAwardSearchRateTable(key, shuffleStrategyAwardSearchRateTable.size(), shuffleStrategyAwardSearchRateTable); + } + + @Override + public Integer getRandomAwardId(Long strategyId) { + // 分布式部署下,不一定为当前应用做的策略装配。也就是值不一定会保存到本应用,而是分布式应用,所以需要从 Redis 中获取。 + int rateRange = repository.getRateRange(strategyId); + // 通过生成的随机值,获取概率值奖品查找表的结果 + return repository.getStrategyAwardAssemble(String.valueOf(strategyId), new SecureRandom().nextInt(rateRange)); + } + + @Override + public Integer getRandomAwardId(Long strategyId, String ruleWeightValue) { + String key = String.valueOf(strategyId).concat("_").concat(ruleWeightValue); + // 分布式部署下,不一定为当前应用做的策略装配。也就是值不一定会保存到本应用,而是分布式应用,所以需要从 Redis 中获取。 + int rateRange = repository.getRateRange(key); + // 通过生成的随机值,获取概率值奖品查找表的结果 + return repository.getStrategyAwardAssemble(key, new SecureRandom().nextInt(rateRange)); + } + + +} diff --git a/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/service/raffle/AbstractRaffleStrategy.java b/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/service/raffle/AbstractRaffleStrategy.java new file mode 100644 index 0000000..a2350e6 --- /dev/null +++ b/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/service/raffle/AbstractRaffleStrategy.java @@ -0,0 +1,78 @@ +package cn.bugstack.domain.strategy.service.raffle; + +import cn.bugstack.domain.strategy.model.entity.RaffleAwardEntity; +import cn.bugstack.domain.strategy.model.entity.RaffleFactorEntity; +import cn.bugstack.domain.strategy.model.entity.RuleActionEntity; +import cn.bugstack.domain.strategy.model.entity.StrategyEntity; +import cn.bugstack.domain.strategy.model.valobj.RuleLogicCheckTypeVO; +import cn.bugstack.domain.strategy.repository.IStrategyRepository; +import cn.bugstack.domain.strategy.service.IRaffleStrategy; +import cn.bugstack.domain.strategy.service.armory.IStrategyDispatch; +import cn.bugstack.domain.strategy.service.rule.factory.DefaultLogicFactory; +import cn.bugstack.types.enums.ResponseCode; +import cn.bugstack.types.exception.AppException; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; + +/** + * @ClassName: AbstractRaffleStrategy + * @Description: 抽奖策略抽象类,定义抽奖的标准流程 + * @Author: zhaoyongfeng + * @Date: 2024/11/17 16:14 + */ +@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. 策略查询 + StrategyEntity strategy = repository.queryStrategyEntityByStrategyId(strategyId); + + // 3. 抽奖前 - 规则过滤 + RuleActionEntity 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 doCheckRaffleBeforeLogic(RaffleFactorEntity raffleFactorEntity, String... logics); + +} diff --git a/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/service/raffle/DefaultRaffleStrategy.java b/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/service/raffle/DefaultRaffleStrategy.java new file mode 100644 index 0000000..05c6c53 --- /dev/null +++ b/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/service/raffle/DefaultRaffleStrategy.java @@ -0,0 +1,84 @@ +package cn.bugstack.domain.strategy.service.raffle; + +import cn.bugstack.domain.strategy.model.entity.RaffleFactorEntity; +import cn.bugstack.domain.strategy.model.entity.RuleActionEntity; +import cn.bugstack.domain.strategy.model.entity.RuleMatterEntity; +import cn.bugstack.domain.strategy.model.valobj.RuleLogicCheckTypeVO; +import cn.bugstack.domain.strategy.repository.IStrategyRepository; +import cn.bugstack.domain.strategy.service.armory.IStrategyDispatch; +import cn.bugstack.domain.strategy.service.rule.ILogicFilter; +import cn.bugstack.domain.strategy.service.rule.factory.DefaultLogicFactory; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +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; + +/** + * @ClassName: DefaultRaffleStrategy + * @Description: 默认的抽奖策略实现 + * @Author: zhaoyongfeng + * @Date: 2024/11/17 16:16 + */ +@Slf4j +@Service +public class DefaultRaffleStrategy extends AbstractRaffleStrategy { + + @Resource + private DefaultLogicFactory logicFactory; + + public DefaultRaffleStrategy(IStrategyRepository repository, IStrategyDispatch strategyDispatch) { + super(repository, strategyDispatch); + } + + @Override + protected RuleActionEntity doCheckRaffleBeforeLogic(RaffleFactorEntity raffleFactorEntity, String... logics) { + Map> 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 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 = logicFilter.filter(ruleMatterEntity); + if (!RuleLogicCheckTypeVO.ALLOW.getCode().equals(ruleActionEntity.getCode())) { + return ruleActionEntity; + } + } + + // 顺序过滤剩余规则 + List ruleList = Arrays.stream(logics) + .filter(s -> !s.equals(DefaultLogicFactory.LogicModel.RULE_BLACKLIST.getCode())) + .collect(Collectors.toList()); + + RuleActionEntity ruleActionEntity = null; + for (String ruleModel : ruleList) { + ILogicFilter 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; + } + +} + diff --git a/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/service/rule/ILogicFilter.java b/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/service/rule/ILogicFilter.java new file mode 100644 index 0000000..741e4d5 --- /dev/null +++ b/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/service/rule/ILogicFilter.java @@ -0,0 +1,17 @@ +package cn.bugstack.domain.strategy.service.rule; + +import cn.bugstack.domain.strategy.model.entity.RuleActionEntity; +import cn.bugstack.domain.strategy.model.entity.RuleMatterEntity; + +/* + * @return + * @author zhaoyongfeng + * @description 抽奖规则过滤接口 + * + * @param null + */ +public interface ILogicFilter { + + RuleActionEntity filter(RuleMatterEntity ruleMatterEntity); + +} diff --git a/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/service/rule/factory/DefaultLogicFactory.java b/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/service/rule/factory/DefaultLogicFactory.java new file mode 100644 index 0000000..cc674b8 --- /dev/null +++ b/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/service/rule/factory/DefaultLogicFactory.java @@ -0,0 +1,52 @@ +package cn.bugstack.domain.strategy.service.rule.factory; + +import cn.bugstack.domain.strategy.model.entity.RuleActionEntity; +import cn.bugstack.domain.strategy.service.annotation.LogicStrategy; +import cn.bugstack.domain.strategy.service.rule.ILogicFilter; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @ClassName: DefaultLogicFactory + * @Description: + * @Author: zhaoyongfeng + * @Date: 2024/11/17 16:05 + */ +@Service +public class DefaultLogicFactory { + public Map> logicFilterMap = new ConcurrentHashMap<>(); + + public DefaultLogicFactory(List> logicFilters) { + logicFilters.forEach(logic -> { + LogicStrategy strategy = AnnotationUtils.findAnnotation(logic.getClass(), LogicStrategy.class); + if (null != strategy) { + logicFilterMap.put(strategy.logicMode().getCode(), logic); + } + }); + } + + public Map> openLogicFilter() { + return (Map>) (Map) logicFilterMap; + } + + @Getter + @AllArgsConstructor + public enum LogicModel { + + RULE_WIGHT("rule_weight","【抽奖前规则】根据抽奖权重返回可抽奖范围KEY"), + RULE_BLACKLIST("rule_blacklist","【抽奖前规则】黑名单规则过滤,命中黑名单则直接返回"), + + ; + + private final String code; + private final String info; + + } + +} diff --git a/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/service/rule/impl/RuleBackListLogicFilter.java b/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/service/rule/impl/RuleBackListLogicFilter.java new file mode 100644 index 0000000..803ef23 --- /dev/null +++ b/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/service/rule/impl/RuleBackListLogicFilter.java @@ -0,0 +1,63 @@ +package cn.bugstack.domain.strategy.service.rule.impl; + +import cn.bugstack.domain.strategy.model.entity.RuleActionEntity; +import cn.bugstack.domain.strategy.model.entity.RuleMatterEntity; +import cn.bugstack.domain.strategy.model.valobj.RuleLogicCheckTypeVO; +import cn.bugstack.domain.strategy.repository.IStrategyRepository; +import cn.bugstack.domain.strategy.service.annotation.LogicStrategy; +import cn.bugstack.domain.strategy.service.rule.ILogicFilter; +import cn.bugstack.domain.strategy.service.rule.factory.DefaultLogicFactory; +import cn.bugstack.types.common.Constants; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; + +/** + * @ClassName: RuleBackListLogicFilter + * @Description: 【抽奖前规则】黑名单用户过滤规则 + * @Author: zhaoyongfeng + * @Date: 2024/11/17 16:07 + */ +@Slf4j +@Component +@LogicStrategy(logicMode = DefaultLogicFactory.LogicModel.RULE_BLACKLIST) +public class RuleBackListLogicFilter implements ILogicFilter { + @Resource + private IStrategyRepository repository; + + @Override + public RuleActionEntity 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]); + + // 过滤其他规则 + String[] userBlackIds = splitRuleValue[1].split(Constants.SPLIT); + for (String userBlackId : userBlackIds) { + if (userId.equals(userBlackId)) { + return RuleActionEntity.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.builder() + .code(RuleLogicCheckTypeVO.ALLOW.getCode()) + .info(RuleLogicCheckTypeVO.ALLOW.getInfo()) + .build(); + } + +} + diff --git a/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/service/rule/impl/RuleWeightLogicFilter.java b/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/service/rule/impl/RuleWeightLogicFilter.java new file mode 100644 index 0000000..845a113 --- /dev/null +++ b/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/service/rule/impl/RuleWeightLogicFilter.java @@ -0,0 +1,105 @@ +package cn.bugstack.domain.strategy.service.rule.impl; + +import cn.bugstack.domain.strategy.model.entity.RuleActionEntity; +import cn.bugstack.domain.strategy.model.entity.RuleMatterEntity; +import cn.bugstack.domain.strategy.model.valobj.RuleLogicCheckTypeVO; +import cn.bugstack.domain.strategy.repository.IStrategyRepository; +import cn.bugstack.domain.strategy.service.annotation.LogicStrategy; +import cn.bugstack.domain.strategy.service.rule.ILogicFilter; +import cn.bugstack.domain.strategy.service.rule.factory.DefaultLogicFactory; +import cn.bugstack.types.common.Constants; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import javax.annotation.Resource; +import java.util.*; + +/** + * @ClassName: RuleWeightLogicFilter + * @Description: 【抽奖前规则】根据抽奖权重返回可抽奖范围KEY + * @Author: zhaoyongfeng + * @Date: 2024/11/17 16:08 + */ +@Slf4j +@Component +@LogicStrategy(logicMode = DefaultLogicFactory.LogicModel.RULE_WIGHT) +public class RuleWeightLogicFilter implements ILogicFilter { + + @Resource + private IStrategyRepository repository; + + public 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 filter(RuleMatterEntity ruleMatterEntity) { + log.info("规则过滤-权重范围 userId:{} strategyId:{} ruleModel:{}", ruleMatterEntity.getUserId(), ruleMatterEntity.getStrategyId(), ruleMatterEntity.getRuleModel()); + + String userId = ruleMatterEntity.getUserId(); + Long strategyId = ruleMatterEntity.getStrategyId(); + String ruleValue = repository.queryStrategyRuleValue(ruleMatterEntity.getStrategyId(), ruleMatterEntity.getAwardId(), ruleMatterEntity.getRuleModel()); + + // 1. 根据用户ID查询用户抽奖消耗的积分值,本章节我们先写死为固定的值。后续需要从数据库中查询。 + Map analyticalValueGroup = getAnalyticalValue(ruleValue); + if (null == analyticalValueGroup || analyticalValueGroup.isEmpty()) { + return RuleActionEntity.builder() + .code(RuleLogicCheckTypeVO.ALLOW.getCode()) + .info(RuleLogicCheckTypeVO.ALLOW.getInfo()) + .build(); + } + + // 2. 转换Keys值,并默认排序 + List 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.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.builder() + .code(RuleLogicCheckTypeVO.ALLOW.getCode()) + .info(RuleLogicCheckTypeVO.ALLOW.getInfo()) + .build(); + } + + private Map getAnalyticalValue(String ruleValue) { + String[] ruleValueGroups = ruleValue.split(Constants.SPACE); + Map 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; + } + +} + + diff --git a/xfg-frame-archetype-lite-infrastructure/src/main/java/cn/bugstack/infrastructure/persistent/dao/IStrategyDao.java b/xfg-frame-archetype-lite-infrastructure/src/main/java/cn/bugstack/infrastructure/persistent/dao/IStrategyDao.java index 8540ee6..f12376a 100644 --- a/xfg-frame-archetype-lite-infrastructure/src/main/java/cn/bugstack/infrastructure/persistent/dao/IStrategyDao.java +++ b/xfg-frame-archetype-lite-infrastructure/src/main/java/cn/bugstack/infrastructure/persistent/dao/IStrategyDao.java @@ -1,10 +1,15 @@ package cn.bugstack.infrastructure.persistent.dao; import cn.bugstack.infrastructure.persistent.po.Award; +import cn.bugstack.infrastructure.persistent.po.Strategy; import org.apache.ibatis.annotations.Mapper; import java.util.List; @Mapper public interface IStrategyDao { - List queryStrategyList(); + List queryStrategyList(); + + Strategy queryStrategyByStrategyId(Long strategyId); + + } diff --git a/xfg-frame-archetype-lite-infrastructure/src/main/java/cn/bugstack/infrastructure/persistent/dao/IStrategyRuleDao.java b/xfg-frame-archetype-lite-infrastructure/src/main/java/cn/bugstack/infrastructure/persistent/dao/IStrategyRuleDao.java index 83d301e..09990d9 100644 --- a/xfg-frame-archetype-lite-infrastructure/src/main/java/cn/bugstack/infrastructure/persistent/dao/IStrategyRuleDao.java +++ b/xfg-frame-archetype-lite-infrastructure/src/main/java/cn/bugstack/infrastructure/persistent/dao/IStrategyRuleDao.java @@ -1,10 +1,16 @@ package cn.bugstack.infrastructure.persistent.dao; import cn.bugstack.infrastructure.persistent.po.Award; +import cn.bugstack.infrastructure.persistent.po.StrategyRule; import org.apache.ibatis.annotations.Mapper; import java.util.List; @Mapper public interface IStrategyRuleDao { - List queryStrategyRuleList(); + List queryStrategyRuleList(); + + StrategyRule queryStrategyRule(StrategyRule strategyRuleReq); + + String queryStrategyRuleValue(StrategyRule strategyRule); + } diff --git a/xfg-frame-archetype-lite-infrastructure/src/main/java/cn/bugstack/infrastructure/persistent/po/Award.java b/xfg-frame-archetype-lite-infrastructure/src/main/java/cn/bugstack/infrastructure/persistent/po/Award.java index d238ca2..4b6f413 100644 --- a/xfg-frame-archetype-lite-infrastructure/src/main/java/cn/bugstack/infrastructure/persistent/po/Award.java +++ b/xfg-frame-archetype-lite-infrastructure/src/main/java/cn/bugstack/infrastructure/persistent/po/Award.java @@ -1,5 +1,6 @@ package cn.bugstack.infrastructure.persistent.po; +import lombok.Builder; import lombok.Data; import java.util.Date; @@ -28,4 +29,5 @@ public class Award { /** 更新时间 */ private Date updateTime; + } diff --git a/xfg-frame-archetype-lite-infrastructure/src/main/java/cn/bugstack/infrastructure/persistent/po/Strategy.java b/xfg-frame-archetype-lite-infrastructure/src/main/java/cn/bugstack/infrastructure/persistent/po/Strategy.java index f2163fa..a76b4ef 100644 --- a/xfg-frame-archetype-lite-infrastructure/src/main/java/cn/bugstack/infrastructure/persistent/po/Strategy.java +++ b/xfg-frame-archetype-lite-infrastructure/src/main/java/cn/bugstack/infrastructure/persistent/po/Strategy.java @@ -1,5 +1,6 @@ package cn.bugstack.infrastructure.persistent.po; +import lombok.Builder; import lombok.Data; import java.util.Date; @@ -11,21 +12,21 @@ import java.util.Date; * @Date: 2024/11/13 20:26 */ @Data +@Builder public class Strategy { - /* ID*/ + /** 自增ID */ private Long id; - - /* 抽奖策略描述*/ + /** 抽奖策略ID */ private Long strategyId; - - /* 抽奖策略ID*/ + /** 抽奖策略描述 */ private String strategyDesc; - - /* 创建时间*/ + /** 抽奖规则模型 */ + private String ruleModels; + /** 创建时间 */ private Date createTime; - - /* 更新时间*/ + /** 更新时间 */ private Date updateTime; + } diff --git a/xfg-frame-archetype-lite-infrastructure/src/main/java/cn/bugstack/infrastructure/persistent/po/StrategyAward.java b/xfg-frame-archetype-lite-infrastructure/src/main/java/cn/bugstack/infrastructure/persistent/po/StrategyAward.java index 83a085f..89c2199 100644 --- a/xfg-frame-archetype-lite-infrastructure/src/main/java/cn/bugstack/infrastructure/persistent/po/StrategyAward.java +++ b/xfg-frame-archetype-lite-infrastructure/src/main/java/cn/bugstack/infrastructure/persistent/po/StrategyAward.java @@ -1,5 +1,6 @@ package cn.bugstack.infrastructure.persistent.po; +import lombok.Builder; import lombok.Data; import java.math.BigDecimal; @@ -38,4 +39,5 @@ public class StrategyAward { /** 修改时间 */ private Date updateTime; + } diff --git a/xfg-frame-archetype-lite-infrastructure/src/main/java/cn/bugstack/infrastructure/persistent/po/StrategyRule.java b/xfg-frame-archetype-lite-infrastructure/src/main/java/cn/bugstack/infrastructure/persistent/po/StrategyRule.java index 1fa5c7e..5d30410 100644 --- a/xfg-frame-archetype-lite-infrastructure/src/main/java/cn/bugstack/infrastructure/persistent/po/StrategyRule.java +++ b/xfg-frame-archetype-lite-infrastructure/src/main/java/cn/bugstack/infrastructure/persistent/po/StrategyRule.java @@ -1,5 +1,6 @@ package cn.bugstack.infrastructure.persistent.po; +import lombok.Builder; import lombok.Data; import java.util.Date; diff --git a/xfg-frame-archetype-lite-infrastructure/src/main/java/cn/bugstack/infrastructure/persistent/repository/StrategyRepository.java b/xfg-frame-archetype-lite-infrastructure/src/main/java/cn/bugstack/infrastructure/persistent/repository/StrategyRepository.java index 5c2355f..136f3e4 100644 --- a/xfg-frame-archetype-lite-infrastructure/src/main/java/cn/bugstack/infrastructure/persistent/repository/StrategyRepository.java +++ b/xfg-frame-archetype-lite-infrastructure/src/main/java/cn/bugstack/infrastructure/persistent/repository/StrategyRepository.java @@ -2,9 +2,15 @@ package cn.bugstack.infrastructure.persistent.repository; import cn.bugstack.domain.strategy.model.entity.StrategyAwardEntity; +import cn.bugstack.domain.strategy.model.entity.StrategyEntity; +import cn.bugstack.domain.strategy.model.entity.StrategyRuleEntity; import cn.bugstack.domain.strategy.repository.IStrategyRepository; import cn.bugstack.infrastructure.persistent.dao.IStrategyAwardDao; +import cn.bugstack.infrastructure.persistent.dao.IStrategyDao; +import cn.bugstack.infrastructure.persistent.dao.IStrategyRuleDao; +import cn.bugstack.infrastructure.persistent.po.Strategy; import cn.bugstack.infrastructure.persistent.po.StrategyAward; +import cn.bugstack.infrastructure.persistent.po.StrategyRule; import cn.bugstack.infrastructure.persistent.redis.IRedisService; import cn.bugstack.types.common.Constants; import org.springframework.stereotype.Repository; @@ -24,6 +30,10 @@ import java.util.Map; @Repository public class StrategyRepository implements IStrategyRepository { + @Resource + private IStrategyDao strategyDao; + @Resource + private IStrategyRuleDao strategyRuleDao; @Resource private IStrategyAwardDao strategyAwardDao; @Resource @@ -53,24 +63,70 @@ public class StrategyRepository implements IStrategyRepository { } @Override - public void storeStrategyAwardSearchRateTable(Long strategyId, Integer rateRange, Map strategyAwardSearchRateTable) { + public void storeStrategyAwardSearchRateTable(String key, Integer rateRange, Map strategyAwardSearchRateTable) { // 1. 存储抽奖策略范围值,如10000,用于生成1000以内的随机数 - redisService.setValue(Constants.RedisKey.STRATEGY_RATE_RANGE_KEY + strategyId, rateRange); + redisService.setValue(Constants.RedisKey.STRATEGY_RATE_RANGE_KEY + key, rateRange); // 2. 存储概率查找表 - Map cacheRateTable = redisService.getMap(Constants.RedisKey.STRATEGY_RATE_TABLE_KEY + strategyId); + Map cacheRateTable = redisService.getMap(Constants.RedisKey.STRATEGY_RATE_TABLE_KEY + key); cacheRateTable.putAll(strategyAwardSearchRateTable); - } @Override - public Integer getStrategyAwardAssemble(Long strategyId, Integer rateKey) { - return redisService.getFromMap(Constants.RedisKey.STRATEGY_RATE_TABLE_KEY + strategyId, rateKey); + public Integer getStrategyAwardAssemble(String key, Integer rateKey) { + return redisService.getFromMap(Constants.RedisKey.STRATEGY_RATE_TABLE_KEY + key, rateKey); } @Override public int getRateRange(Long strategyId) { - return redisService.getValue(Constants.RedisKey.STRATEGY_RATE_RANGE_KEY + strategyId); + return getRateRange(String.valueOf(strategyId)); + } + + @Override + public int getRateRange(String key) { + return redisService.getValue(Constants.RedisKey.STRATEGY_RATE_RANGE_KEY + key); + } + + @Override + public StrategyEntity queryStrategyEntityByStrategyId(Long strategyId) { + // 优先从缓存获取 + String cacheKey = Constants.RedisKey.STRATEGY_KEY + strategyId; + StrategyEntity strategyEntity = redisService.getValue(cacheKey); + if (null != strategyEntity) return strategyEntity; + Strategy strategy = strategyDao.queryStrategyByStrategyId(strategyId); + strategyEntity = StrategyEntity.builder() + .strategyId(strategy.getStrategyId()) + .strategyDesc(strategy.getStrategyDesc()) + .ruleModels(strategy.getRuleModels()) + .build(); + redisService.setValue(cacheKey, strategyEntity); + return strategyEntity; + } + + @Override + public StrategyRuleEntity queryStrategyRule(Long strategyId, String ruleModel) { + StrategyRule strategyRuleReq = new StrategyRule(); + strategyRuleReq.setStrategyId(strategyId); + strategyRuleReq.setRuleModel(ruleModel); + StrategyRule strategyRuleRes = strategyRuleDao.queryStrategyRule(strategyRuleReq); + return StrategyRuleEntity.builder() + .strategyId(strategyRuleRes.getStrategyId()) + .awardId(strategyRuleRes.getAwardId()) + .ruleType(strategyRuleRes.getRuleType()) + .ruleModel(strategyRuleRes.getRuleModel()) + .ruleValue(strategyRuleRes.getRuleValue()) + .ruleDesc(strategyRuleRes.getRuleDesc()) + .build(); + } + + @Override + public String queryStrategyRuleValue(Long strategyId, Integer awardId, String ruleModel) { + StrategyRule strategyRule = new StrategyRule(); + strategyRule.setStrategyId(strategyId); + strategyRule.setAwardId(awardId); + strategyRule.setRuleModel(ruleModel); + return strategyRuleDao.queryStrategyRuleValue(strategyRule); } } + diff --git a/xfg-frame-archetype-lite-types/src/main/java/cn/bugstack/types/common/Constants.java b/xfg-frame-archetype-lite-types/src/main/java/cn/bugstack/types/common/Constants.java index 008bc32..72232de 100644 --- a/xfg-frame-archetype-lite-types/src/main/java/cn/bugstack/types/common/Constants.java +++ b/xfg-frame-archetype-lite-types/src/main/java/cn/bugstack/types/common/Constants.java @@ -3,11 +3,16 @@ package cn.bugstack.types.common; public class Constants { public final static String SPLIT = ","; + public final static String SPACE = " "; + public final static String COLON = ":"; + public static class RedisKey { + public static String STRATEGY_KEY = "big_market_strategy_key_"; public static String STRATEGY_AWARD_KEY = "big_market_strategy_award_key_"; public static String STRATEGY_RATE_TABLE_KEY = "big_market_strategy_rate_table_key_"; public static String STRATEGY_RATE_RANGE_KEY = "big_market_strategy_rate_range_key_"; + } diff --git a/xfg-frame-archetype-lite-types/src/main/java/cn/bugstack/types/enums/ResponseCode.java b/xfg-frame-archetype-lite-types/src/main/java/cn/bugstack/types/enums/ResponseCode.java index 7460d4b..15eaba2 100644 --- a/xfg-frame-archetype-lite-types/src/main/java/cn/bugstack/types/enums/ResponseCode.java +++ b/xfg-frame-archetype-lite-types/src/main/java/cn/bugstack/types/enums/ResponseCode.java @@ -12,9 +12,11 @@ public enum ResponseCode { SUCCESS("0000", "成功"), UN_ERROR("0001", "未知失败"), ILLEGAL_PARAMETER("0002", "非法参数"), + STRATEGY_RULE_WEIGHT_IS_NULL("ERR_BIZ_001", "业务异常,策略规则中 rule_weight 权重规则已适用但未配置"), ; private String code; private String info; + } -- GitLab