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 f160d79406c368130a8559f118816bbb0e182ba4..6c8f8bb4f77018284f68edba08865e65cdf05ec7 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
@@ -24,5 +24,12 @@
from strategy_rule
where strategy_id = #{strategyId} and rule_model = #{ruleModel}
+
diff --git a/xfg-frame-archetype-lite-app/src/test/java/cn/bugstack/test/ApiTest.java b/xfg-frame-archetype-lite-app/src/test/java/cn/bugstack/test/ApiTest.java
index 9bc1a201d91950bc85a3ad92354618d6b3c5f2a0..181edd5a6e9667d8a2839014f5617e8a1926fb09 100644
--- a/xfg-frame-archetype-lite-app/src/test/java/cn/bugstack/test/ApiTest.java
+++ b/xfg-frame-archetype-lite-app/src/test/java/cn/bugstack/test/ApiTest.java
@@ -16,4 +16,11 @@ public class ApiTest {
log.info("测试完成");
}
+ @Test
+ public void test1() {
+ String testSplit = "abc 123";
+ String[] s = testSplit.split("d");
+ System.out.println(s[0]);
+ }
+
}
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 0000000000000000000000000000000000000000..1595037998b2dd9d88127d762c17ed47d0bc537f
--- /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;
+
+/**
+ * @author Fuzhengwei bugstack.cn @小傅哥
+ * @description 抽奖策略测试
+ * @create 2024-01-06 13:28
+ */
+@Slf4j
+@RunWith(SpringRunner.class)
+@SpringBootTest
+public class RaffleStrategyTest {
+
+ @Resource
+ private IRaffleStrategy raffleStrategy;
+
+ @Resource
+ private RuleWeightLogicFilter ruleWeightLogicFilter;
+
+ @Before
+ public void setUp() {
+ ReflectionTestUtils.setField(ruleWeightLogicFilter, "userScore", 40500L);
+ }
+
+ @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-domain/src/main/java/cn/bugstack/domain/strategy/model/entity/AwardEntity.java b/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/model/entity/AwardEntity.java
new file mode 100644
index 0000000000000000000000000000000000000000..d78307cdd8059916fc0ee34155a3f31096799bb3
--- /dev/null
+++ b/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/model/entity/AwardEntity.java
@@ -0,0 +1,25 @@
+package cn.bugstack.domain.strategy.model.entity;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * @description: 策略结果实体
+ * @author: hdr
+ * @PACKAGE_NAME: cn.bugstack.domain.strategy.model.entity
+ * @DATE: 2024/4/12
+ */
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class AwardEntity {
+
+ /** 用户ID */
+ private String userId;
+ /** 奖品ID */
+ private Integer awardId;
+
+}
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 0000000000000000000000000000000000000000..ca96da47c1fa83c528a3d98cbd411304aa0c3070
--- /dev/null
+++ b/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/model/entity/RaffleAwardEntity.java
@@ -0,0 +1,44 @@
+package cn.bugstack.domain.strategy.model.entity;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * @description: 抽奖奖品实体
+ * @author: hdr
+ * @PACKAGE_NAME: cn.bugstack.domain.strategy.model.entity
+ * @DATE: 2024/4/12
+ */
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+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 0000000000000000000000000000000000000000..04c2a2078bbe53bd07c33865a3e19f7aab551074
--- /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;
+
+/**
+ * @description: 抽奖因子实体
+ * @author: hdr
+ * @PACKAGE_NAME: cn.bugstack.domain.strategy.model.entity
+ * @DATE: 2024/4/12
+ */
+
+@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 0000000000000000000000000000000000000000..d15af889d74abb750a35a559cee5b452f640af45
--- /dev/null
+++ b/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/model/entity/RuleActionEntity.java
@@ -0,0 +1,59 @@
+package cn.bugstack.domain.strategy.model.entity;
+
+import cn.bugstack.domain.strategy.model.valobj.RuleLogicCheckTypeVO;
+import lombok.*;
+
+/**
+ * @description: 规则动作实体
+ * @author: hdr
+ * @PACKAGE_NAME: cn.bugstack.domain.strategy.model.entity
+ * @DATE: 2024/4/12
+ */
+@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 0000000000000000000000000000000000000000..770126b766b01fe7676d2b3a37e9473d70901ac0
--- /dev/null
+++ b/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/model/entity/RuleMatterEntity.java
@@ -0,0 +1,23 @@
+package cn.bugstack.domain.strategy.model.entity;
+
+import lombok.Data;
+
+/**
+ * @description: 规则物料实体对象,用于过滤规则的必要参数信息。
+ * @author: hdr
+ * @PACKAGE_NAME: cn.bugstack.domain.strategy.model.entity
+ * @DATE: 2024/4/12
+ */
+@Data
+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/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 0000000000000000000000000000000000000000..0c3735ffb204a60ba86ff36201a7065db9d7a319
--- /dev/null
+++ b/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/model/valobj/RuleLogicCheckTypeVO.java
@@ -0,0 +1,23 @@
+package cn.bugstack.domain.strategy.model.valobj;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * @description: 规则过滤校验类型值对象
+ * @author: hdr
+ * @PACKAGE_NAME: cn.bugstack.domain.strategy.model.entity.valobj
+ * @DATE: 2024/4/12
+ */
+@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 d0908f65aa14649979a548576955427d069c5d0e..4e5d63397ec4a3cc93c72af8d0b462ceb215b93e 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
@@ -67,4 +67,13 @@ public interface IStrategyRepository {
* @return
*/
int getRateRange(String key);
+
+ /**
+ * 查询策略规则值
+ * @param strategyId 策略id
+ * @param awardId 奖品id
+ * @param ruleModel 规则模型
+ * @return
+ */
+ 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 0000000000000000000000000000000000000000..19eb4fb968ed8f34c0b47a93249171b600211edb
--- /dev/null
+++ b/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/service/IRaffleStrategy.java
@@ -0,0 +1,20 @@
+package cn.bugstack.domain.strategy.service;
+
+import cn.bugstack.domain.strategy.model.entity.RaffleAwardEntity;
+import cn.bugstack.domain.strategy.model.entity.RaffleFactorEntity;
+
+/**
+ * @description: 抽奖策略接口
+ * @author: hdr
+ * @PACKAGE_NAME: cn.bugstack.domain.strategy.service
+ * @DATE: 2024/4/11
+ */
+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 0000000000000000000000000000000000000000..7a5d9ea7ff771da5f99b815e44d013ae8500d207
--- /dev/null
+++ b/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/service/annotation/LogicStrategy.java
@@ -0,0 +1,21 @@
+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;
+
+/**
+ * @author Fuzhengwei bugstack.cn @小傅哥
+ * @description 策略自定义枚举
+ * @create 2023-12-31 11:29
+ */
+@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/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 0000000000000000000000000000000000000000..bdba2ba7badd4ea312ad9cc64d4fbb80d14ba01f
--- /dev/null
+++ b/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/service/raffle/AbstractRaffleStrategy.java
@@ -0,0 +1,86 @@
+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.domain.strategy.service.rule.impl.RuleBackListLogicFilter;
+import cn.bugstack.types.enums.ResponseCode;
+import cn.bugstack.types.exception.AppException;
+import com.alibaba.fastjson.JSON;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ * @description:
+ * @author: hdr
+ * @PACKAGE_NAME: cn.bugstack.domain.strategy.service.raffle
+ * @DATE: 2024/4/12
+ */
+@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());
+ log.info("ruleActionEntity:{}", JSON.toJSONString(ruleActionEntity));
+
+ 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())) {
+ // 权重根据返回的信息进行抽奖
+ System.out.println("权重抽奖");
+ RuleActionEntity.RaffleBeforeEntity raffleBeforeEntity = ruleActionEntity.getData();
+ String ruleWeightValueKey = raffleBeforeEntity.getRuleWeightValueKey();
+ Integer awardId = strategyDispatch.getRandomAwardId(strategyId,ruleWeightValueKey);
+ return RaffleAwardEntity.builder()
+ .awardId(awardId)
+ .build();
+ }
+ }
+
+ // 4. 默认抽奖流程
+ System.out.println("默认抽奖");
+ Integer awardId = strategyDispatch.getRandomAwardId(strategyId);
+
+ return RaffleAwardEntity.builder()
+ .awardId(awardId)
+ .build();
+ }
+
+ protected abstract RuleActionEntity doCheckRaffleBeforeLogic(RaffleFactorEntity factorEntity, 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 0000000000000000000000000000000000000000..9ae0dcb86f29f64f87aafd5928efb0fa3b0d2ac4
--- /dev/null
+++ b/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/service/raffle/DefaultRaffleStrategy.java
@@ -0,0 +1,88 @@
+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 com.alibaba.fastjson.JSON;
+import com.sun.deploy.security.ruleset.RuleAction;
+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;
+
+/**
+ * @description:
+ * @author: hdr
+ * @PACKAGE_NAME: cn.bugstack.domain.strategy.service.raffle
+ * @DATE: 2024/4/12
+ */
+@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) {
+
+ log.info("raffleFactorEntity:{},logics:{}", JSON.toJSONString(raffleFactorEntity),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(str-> !str.contains(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());
+ // todo 数据不对
+ ruleMatterEntity.setAwardId(ruleMatterEntity.getAwardId());
+ ruleMatterEntity.setStrategyId(raffleFactorEntity.getStrategyId());
+ ruleMatterEntity.setRuleModel(ruleModel);
+ ruleActionEntity = logicFilter.filter(ruleMatterEntity);
+ log.info("ruleActionEntity:{}",JSON.toJSONString(ruleActionEntity));
+ 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 0000000000000000000000000000000000000000..f7dc29c8b249f1be412bf0b4ebb9820a3485928c
--- /dev/null
+++ b/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/service/rule/ILogicFilter.java
@@ -0,0 +1,16 @@
+package cn.bugstack.domain.strategy.service.rule;
+
+import cn.bugstack.domain.strategy.model.entity.RuleActionEntity;
+import cn.bugstack.domain.strategy.model.entity.RuleMatterEntity;
+
+/**
+ * @description: 抽奖规则过滤接口
+ * @author: hdr
+ * @PACKAGE_NAME: cn.bugstack.domain.strategy.service.rule
+ * @DATE: 2024/4/12
+ */
+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 0000000000000000000000000000000000000000..4cabdff5a2d6b7eced136afed89700db3e695ef6
--- /dev/null
+++ b/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/service/rule/factory/DefaultLogicFactory.java
@@ -0,0 +1,53 @@
+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;
+
+/**
+ * @description: 规则工厂
+ * @author: hdr
+ * @PACKAGE_NAME: cn.bugstack.domain.strategy.service.rule.factory
+ * @DATE: 2024/4/12
+ */
+@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 0000000000000000000000000000000000000000..934bd7c3b62ba2c7ac6df8c6112d286af1728780
--- /dev/null
+++ b/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/service/rule/impl/RuleBackListLogicFilter.java
@@ -0,0 +1,65 @@
+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;
+
+/**
+ * @description: [抽奖前规则]黑名单用户过滤规则
+ * @author: hdr
+ * @PACKAGE_NAME: cn.bugstack.domain.strategy.service.rule.impl
+ * @DATE: 2024/4/12
+ */
+@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();
+
+ // 100:user001,user002,user003
+ // 查询规则值配置
+ 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 0000000000000000000000000000000000000000..a8a51a7d214a9af9bf20ebbbf984cb823eaf4522
--- /dev/null
+++ b/xfg-frame-archetype-lite-domain/src/main/java/cn/bugstack/domain/strategy/service/rule/impl/RuleWeightLogicFilter.java
@@ -0,0 +1,102 @@
+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.*;
+
+/**
+ * @description: [抽奖前规则] 根据抽奖鉴权重返可抽奖范围key
+ * @author: hdr
+ * @PACKAGE_NAME: cn.bugstack.domain.strategy.service.rule.impl
+ * @DATE: 2024/4/12
+ */
+@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 ruleValueGroup : ruleValueGroups) {
+ // 检查驶入是否为空
+ if (ruleValueGroup == null || ruleValueGroup.isEmpty()) {
+ return ruleValueMap;
+ }
+
+ // 分割字符串以获取键和值
+ String[] parts = ruleValueGroup.split(Constants.COLON);
+ if (parts.length < 2) {
+ throw new IllegalArgumentException("rule_weight rule_rule invalid input format" + ruleValueGroup);
+ }
+ ruleValueMap.put(Long.parseLong(parts[0]),ruleValueGroup);
+ }
+ return ruleValueMap;
+ }
+}
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 d37bbe74127db6dd5c6fe1ad15721b6c98749274..1ac180f6788beb5870f49e5f0c97846843a09328 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
@@ -21,4 +21,11 @@ public interface IStrategyRuleDao {
* @return
*/
StrategyRule queryStrategyRule(StrategyRule strategyRuleReq);
+
+ /**
+ * 查询规则值
+ * @param strategyRule
+ * @return
+ */
+ String queryStrategyRuleValue(StrategyRule strategyRule);
}
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 40069d7d98df4b0201a404dc39e7da9f5c4eb34a..6df15a91cbe5df65ed06472d673eb966f5705f87 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
@@ -143,4 +143,13 @@ public class StrategyRepository implements IStrategyRepository {
return redisService.getValue(Constants.RedisKey.STRATEGY_RATE_RANGE_KEY + key);
}
+ @Override
+ public String queryStrategyRuleValue(Long strategyId, Integer awardId, String ruleModel) {
+ StrategyRule strategyRule = new StrategyRule();
+ strategyRule.setStrategyId(strategyId);
+ strategyRule.setRuleModel(ruleModel);
+ strategyRule.setAwardId(awardId);
+ return strategyRuleDao.queryStrategyRuleValue(strategyRule);
+ }
+
}