提交 1f04a8c8 编写于 作者: H hudingrong

改写成职责链

上级 caf38ce3
package cn.bugstack.test.domain;
import cn.bugstack.domain.strategy.service.armory.IStrategyArmory;
import cn.bugstack.domain.strategy.service.rule.chain.ILogicChain;
import cn.bugstack.domain.strategy.service.rule.chain.factory.DefaultChainFactory;
import cn.bugstack.domain.strategy.service.rule.chain.impl.RuleWeightLogicChain;
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-20 11:20
*/
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest
public class LogicChainTest {
@Resource
private IStrategyArmory strategyArmory;
@Resource
private RuleWeightLogicChain ruleWeightLogicChain;
@Resource
private DefaultChainFactory defaultChainFactory;
@Before
public void setUp() {
// 策略装配 100001、100002、100003
log.info("测试结果:{}", strategyArmory.assembleLotteryStrategy(100001L));
log.info("测试结果:{}", strategyArmory.assembleLotteryStrategy(100002L));
log.info("测试结果:{}", strategyArmory.assembleLotteryStrategy(100003L));
}
@Test
public void test_LogicChain_rule_blacklist() {
ILogicChain logicChain = defaultChainFactory.openLogicChain(100003L);
Integer awardId = logicChain.logic("user001", 100003L);
log.info("测试结果:{}", awardId);
}
@Test
public void test_LogicChain_rule_weight() {
// 通过反射 mock 规则中的值
ReflectionTestUtils.setField(ruleWeightLogicChain, "userScore", 4900L);
ILogicChain logicChain = defaultChainFactory.openLogicChain(100001L);
Integer awardId = logicChain.logic("xiaofuge", 100001L);
log.info("测试结果:{}", awardId);
}
@Test
public void test_LogicChain_rule_default() {
ILogicChain logicChain = defaultChainFactory.openLogicChain(100001L);
Integer awardId = logicChain.logic("xiaofuge", 100001L);
log.info("测试结果:{}", awardId);
}
}
......@@ -4,9 +4,8 @@ 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.armory.IStrategyArmory;
import cn.bugstack.domain.strategy.service.armory.StrategyArmory;
import cn.bugstack.domain.strategy.service.rule.impl.RuleLockLogicFilter;
import cn.bugstack.domain.strategy.service.rule.impl.RuleWeightLogicFilter;
import cn.bugstack.domain.strategy.service.rule.filter.impl.RuleLockLogicFilter;
import cn.bugstack.domain.strategy.service.rule.filter.impl.RuleWeightLogicFilter;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.junit.Before;
......@@ -74,7 +73,7 @@ public class RaffleStrategyTest {
public void test_performRaffle_blacklist() {
RaffleFactorEntity raffleFactorEntity = RaffleFactorEntity.builder()
.userId("user003") // 黑名单用户 user001,user002,user003
.strategyId(100001L)
.strategyId(100003L)
.build();
RaffleAwardEntity raffleAwardEntity = raffleStrategy.performRaffle(raffleFactorEntity);
......
package cn.bugstack.domain.strategy.model.valobj;
import cn.bugstack.domain.strategy.service.rule.factory.DefaultLogicFactory;
import cn.bugstack.domain.strategy.service.rule.filter.factory.DefaultLogicFactory;
import cn.bugstack.types.common.Constants;
import lombok.AllArgsConstructor;
import lombok.Builder;
......
......@@ -69,6 +69,14 @@ public interface IStrategyRepository {
*/
int getRateRange(String key);
/**
* 查询策略规则值
* @param strategyId 策略jd
* @param ruleModel 奖品id
* @return 策略规则
*/
String queryStrategyRuleValue(Long strategyId, String ruleModel);
/**
* 查询策略规则值
* @param strategyId 策略id
......
package cn.bugstack.domain.strategy.service.annotation;
import cn.bugstack.domain.strategy.service.rule.factory.DefaultLogicFactory;
import cn.bugstack.domain.strategy.service.rule.filter.factory.DefaultLogicFactory;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
......
......@@ -9,11 +9,11 @@ import cn.bugstack.domain.strategy.model.valobj.StrategyAwardRuleModelVO;
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.domain.strategy.service.rule.chain.ILogicChain;
import cn.bugstack.domain.strategy.service.rule.chain.factory.DefaultChainFactory;
import cn.bugstack.domain.strategy.service.rule.filter.factory.DefaultLogicFactory;
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;
......@@ -30,10 +30,13 @@ public abstract class AbstractRaffleStrategy implements IRaffleStrategy {
protected IStrategyRepository repository;
// 策略调度服务 -> 只负责抽奖处理,通过新增接口的方式,隔离职责,不需要使用方关心或者调用抽奖的初始化
protected IStrategyDispatch strategyDispatch;
// 抽奖的职责链 -> 抽奖的规则中,解耦出前置规则为职责链处理
protected final DefaultChainFactory defaultChainFactory;
public AbstractRaffleStrategy(IStrategyRepository repository, IStrategyDispatch strategyDispatch) {
public AbstractRaffleStrategy(IStrategyRepository repository, IStrategyDispatch strategyDispatch, DefaultChainFactory defaultChainFactory) {
this.repository = repository;
this.strategyDispatch = strategyDispatch;
this.defaultChainFactory = defaultChainFactory;
}
@Override
......@@ -46,6 +49,13 @@ public abstract class AbstractRaffleStrategy implements IRaffleStrategy {
throw new AppException(ResponseCode.ILLEGAL_PARAMETER.getCode(), ResponseCode.ILLEGAL_PARAMETER.getInfo());
}
// // 2. 获取抽奖职责链 -> 前置规则的责任链处理
// ILogicChain logicChain = defaultChainFactory.openLogicChain(strategyId);
//
// // 3. 通过职责链获取,获奖id
// Integer awardId = logicChain.logic(userId, strategyId);
// 2. 策略查询
StrategyEntity strategy = repository.queryStrategyEntityByStrategyId(strategyId) ;
......
......@@ -7,9 +7,9 @@ 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 cn.bugstack.domain.strategy.service.rule.chain.factory.DefaultChainFactory;
import cn.bugstack.domain.strategy.service.rule.filter.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;
......@@ -33,8 +33,8 @@ public class DefaultRaffleStrategy extends AbstractRaffleStrategy{
@Resource
private DefaultLogicFactory logicFactory;
public DefaultRaffleStrategy(IStrategyRepository repository, IStrategyDispatch strategyDispatch) {
super(repository, strategyDispatch);
public DefaultRaffleStrategy(IStrategyRepository repository, IStrategyDispatch strategyDispatch, DefaultChainFactory defaultChainFactory) {
super(repository, strategyDispatch, defaultChainFactory);
}
@Override
......
package cn.bugstack.domain.strategy.service.rule.chain;
import lombok.extern.slf4j.Slf4j;
/**
* @description: 抽奖策略责任链,判断走那种抽奖策略。如;默认抽象、权重抽奖、黑名单抽奖
* @author: hdr
* @PACKAGE_NAME: cn.bugstack.domain.strategy.service.rule.chain
* @DATE: 2024/4/19
*/
@Slf4j
public abstract class AbstractLogicChain implements ILogicChain {
private ILogicChain next;
@Override
public ILogicChain next() {
return next;
}
@Override
public ILogicChain appendNext(ILogicChain next) {
this.next = next;
return next;
}
protected abstract String ruleModel();
}
package cn.bugstack.domain.strategy.service.rule.chain;
/**
* @description: 抽奖策略规则责任链接口
* @author: hdr
* @PACKAGE_NAME: cn.bugstack.domain.strategy.service.rule.chain
* @DATE: 2024/4/19
*/
public interface ILogicChain extends ILogicChainArmory {
/**
* 责任链接口
*
* @param userId 用户id
* @param strategyId 策略id
* @return 奖品id
*/
Integer logic(String userId, Long strategyId);
}
package cn.bugstack.domain.strategy.service.rule.chain;
/**
* @description: 责任链装配
* @author: hdr
* @PACKAGE_NAME: cn.bugstack.domain.strategy.service.rule.chain
* @DATE: 2024/4/19
*/
public interface ILogicChainArmory {
ILogicChain next();
ILogicChain appendNext(ILogicChain next);
}
package cn.bugstack.domain.strategy.service.rule.chain.factory;
import cn.bugstack.domain.strategy.model.entity.StrategyEntity;
import cn.bugstack.domain.strategy.repository.IStrategyRepository;
import cn.bugstack.domain.strategy.service.rule.chain.ILogicChain;
import org.springframework.stereotype.Service;
import java.util.Map;
/**
* @description: 装配工厂
* @author: hdr
* @PACKAGE_NAME: cn.bugstack.domain.strategy.service.rule.chain.factory
* @DATE: 2024/4/19
*/
@Service
public class DefaultChainFactory {
private final Map<String, ILogicChain> logicChainGroup;
protected IStrategyRepository repository;
public DefaultChainFactory(Map<String, ILogicChain> logicChainGroup, IStrategyRepository repository) {
this.logicChainGroup = logicChainGroup;
this.repository = repository;
}
public ILogicChain openLogicChain(Long strategyId) {
StrategyEntity strategy = repository.queryStrategyEntityByStrategyId(strategyId);
String[] ruleModels = strategy.ruleModels();
// 如果未配置策略规则,则值装配一个默认职责链
if (null == ruleModels ||0 == ruleModels.length) {
return logicChainGroup.get("default");
}
// 按照配置顺序填装用户配置的责任链:rule_blacklist、rule_weight 「注意此数据从Redis缓存中获取,如果更新库表,记得在测试阶段手动处理缓存」
ILogicChain logicChain = logicChainGroup.get(ruleModels[0]);
ILogicChain current = logicChain;
for (int i = 1; i < ruleModels.length; i++) {
ILogicChain nextChain = logicChainGroup.get(ruleModels[i]);
}
// 责任链的最后填装默认责任链
current.appendNext(logicChainGroup.get("default"));
return logicChain;
}
}
package cn.bugstack.domain.strategy.service.rule.chain.impl;
import cn.bugstack.domain.strategy.repository.IStrategyRepository;
import cn.bugstack.domain.strategy.service.rule.chain.AbstractLogicChain;
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.chain.impl
* @DATE: 2024/4/19
*/
@Slf4j
@Component("rule_blacklist")
public class BackListLogicChain extends AbstractLogicChain {
@Resource
private IStrategyRepository repository;
@Override
public Integer logic(String userId, Long strategyId) {
log.info("抽奖责任链-黑名单开始 userId: {} strategyId: {} ruleModel: {}", userId, strategyId, ruleModel());
// 查询规则值配置
String ruleValue = repository.queryStrategyRuleValue(strategyId,ruleModel());
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)) {
log.info("抽奖责任链-黑名单接管 userId: {} strategyId: {} ruleModel: {} awardId: {}", userId, strategyId, ruleModel(), awardId);
return awardId;
}
}
// 过滤其他责任链
log.info("抽奖责任链-黑名单放行 userId: {} strategyId: {} ruleModel: {}", userId, strategyId, ruleModel());
return next().logic(userId, strategyId);
}
@Override
protected String ruleModel() {
return "rule_blacklist";
}
}
package cn.bugstack.domain.strategy.service.rule.chain.impl;
import cn.bugstack.domain.strategy.service.armory.IStrategyDispatch;
import cn.bugstack.domain.strategy.service.rule.chain.AbstractLogicChain;
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.chain.impl
* @DATE: 2024/4/19
*/
@Slf4j
@Component("default")
public class DefaultLogicChain extends AbstractLogicChain {
@Resource
protected IStrategyDispatch strategyDispatch;
@Override
protected String ruleModel() {
return "default";
}
@Override
public Integer logic(String userId, Long strategyId) {
Integer awardId = strategyDispatch.getRandomAwardId(strategyId);
log.info("抽奖责任链-默认处理 userId: {} strategyId: {} ruleModel: {} awardId: {}", userId, strategyId, ruleModel(), awardId);
return awardId;
}
}
package cn.bugstack.domain.strategy.service.rule.chain.impl;
import cn.bugstack.domain.strategy.repository.IStrategyRepository;
import cn.bugstack.domain.strategy.service.armory.IStrategyDispatch;
import cn.bugstack.domain.strategy.service.rule.chain.AbstractLogicChain;
import cn.bugstack.types.common.Constants;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.*;
/**
* @description:
* @author: hdr
* @PACKAGE_NAME: cn.bugstack.domain.strategy.service.rule.chain.impl
* @DATE: 2024/4/19
*/
@Slf4j
@Component("rule_weight")
public class RuleWeightLogicChain extends AbstractLogicChain {
@Resource
private IStrategyRepository repository;
@Resource
private IStrategyDispatch strategyDispatch;
public Long userScore = 0L;
@Override
protected String ruleModel() {
return "rule_weight";
}
/**
* 权重责任链过滤;
* 1. 权重规则格式;4000:102,103,104,105 5000:102,103,104,105,106,107 6000:102,103,104,105,106,107,108,109
* 2. 解析数据格式;判断哪个范围符合用户的特定抽奖范围
*/
@Override
public Integer logic(String userId, Long strategyId) {
log.info("抽奖责任链-权重开始 userId: {} strategyId: {} ruleModel: {}", userId, strategyId, ruleModel());
String ruleValue = repository.queryStrategyRuleValue(strategyId, ruleModel());
// 1. 根据用户ID查询用户抽奖小号的积分值
Map<Long, String> analyticalValueGroup = getAnalyticalValue(ruleValue);
if (null == analyticalValueGroup || analyticalValueGroup.isEmpty()) {
return null;
}
// 2. 装换keys值,并默认排序
List<Long> analyticalSortedKeys = new ArrayList<>(analyticalValueGroup.keySet());
Collections.sort(analyticalSortedKeys);
// 3. 找出最小符合的值,也就是【4500 积分,能找到 4000:102,103,104,105】、【5000 积分,能找到 5000:102,103,104,105,106,107】
Long nextValue = analyticalSortedKeys.stream()
.sorted(Comparator.reverseOrder())
.filter(analyticalSortedKeyValue-> userScore >= analyticalSortedKeyValue)
.findFirst()
.orElse(null);
// 4. 权重抽奖
if (null != nextValue) {
Integer awardId = strategyDispatch.getRandomAwardId(strategyId, analyticalValueGroup.get(nextValue));
log.info("抽奖责任链-权重接管 userId: {} strategyId: {} ruleModel: {} awardId: {}", userId, strategyId, ruleModel(), awardId);
return awardId;
}
// 5. 过滤其他责任链
log.info("抽奖责任链-权重放行 userId: {} strategyId: {} ruleModel: {}", userId, strategyId, ruleModel());
return next().logic(userId, strategyId);
}
private Map<Long, String> getAnalyticalValue(String ruleValue) {
String[] ruleGroups = ruleValue.split(Constants.SPACE);
log.info("ruleGroups:{}",ruleGroups);
Map<Long, String> ruleValueMap = new HashMap<>();
for (String ruleValueKey : ruleGroups) {
// 判断是否有值
if (ruleValueKey == null || ruleValueKey.isEmpty()) {
return ruleValueMap;
}
// 分割字符串以获取键和值
String[] parts = ruleValueKey.split(Constants.COLON);
log.info("parts:{},ruleValue:{}", JSON.toJSONString(parts),ruleValue);
if (parts.length < 2) {
throw new IllegalArgumentException("rule_weight rule_rule invalid input format" + ruleValueKey);
}
ruleValueMap.put(Long.parseLong(parts[0]), ruleValueKey);
}
return ruleValueMap;
}
}
package cn.bugstack.domain.strategy.service.rule.factory;
package cn.bugstack.domain.strategy.service.rule.filter.factory;
import cn.bugstack.domain.strategy.model.entity.RuleActionEntity;
import cn.bugstack.domain.strategy.service.annotation.LogicStrategy;
......@@ -15,7 +15,7 @@ import java.util.concurrent.ConcurrentHashMap;
/**
* @description: 规则工厂
* @author: hdr
* @PACKAGE_NAME: cn.bugstack.domain.strategy.service.rule.factory
* @PACKAGE_NAME: cn.bugstack.domain.strategy.service.rule.filter.factory
* @DATE: 2024/4/12
*/
@Service
......@@ -57,7 +57,6 @@ public class DefaultLogicFactory {
public static boolean isAfter(String code){
return "after".equals(LogicModel.valueOf(code.toUpperCase()).type);
}
}
}
package cn.bugstack.domain.strategy.service.rule.impl;
package cn.bugstack.domain.strategy.service.rule.filter.impl;
import cn.bugstack.domain.strategy.model.entity.RuleActionEntity;
import cn.bugstack.domain.strategy.model.entity.RuleMatterEntity;
......@@ -6,7 +6,7 @@ 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.domain.strategy.service.rule.filter.factory.DefaultLogicFactory;
import cn.bugstack.types.common.Constants;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
......@@ -16,7 +16,7 @@ import javax.annotation.Resource;
/**
* @description: [抽奖前规则]黑名单用户过滤规则
* @author: hdr
* @PACKAGE_NAME: cn.bugstack.domain.strategy.service.rule.impl
* @PACKAGE_NAME: cn.bugstack.domain.strategy.service.rule.filter.impl
* @DATE: 2024/4/12
*/
@Slf4j
......
package cn.bugstack.domain.strategy.service.rule.impl;
package cn.bugstack.domain.strategy.service.rule.filter.impl;
import cn.bugstack.domain.strategy.model.entity.RuleActionEntity;
import cn.bugstack.domain.strategy.model.entity.RuleMatterEntity;
......@@ -6,7 +6,7 @@ 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.domain.strategy.service.rule.filter.factory.DefaultLogicFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
......
package cn.bugstack.domain.strategy.service.rule.impl;
package cn.bugstack.domain.strategy.service.rule.filter.impl;
import cn.bugstack.domain.strategy.model.entity.RuleActionEntity;
import cn.bugstack.domain.strategy.model.entity.RuleMatterEntity;
......@@ -6,7 +6,7 @@ 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.domain.strategy.service.rule.filter.factory.DefaultLogicFactory;
import cn.bugstack.types.common.Constants;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
......@@ -17,7 +17,7 @@ import java.util.*;
/**
* @description: [抽奖前规则] 根据抽奖鉴权重返可抽奖范围key
* @author: hdr
* @PACKAGE_NAME: cn.bugstack.domain.strategy.service.rule.impl
* @PACKAGE_NAME: cn.bugstack.domain.strategy.service.rule.filter.impl
* @DATE: 2024/4/12
*/
@Slf4j
......
......@@ -158,6 +158,11 @@ public class StrategyRepository implements IStrategyRepository {
return redisService.getValue(Constants.RedisKey.STRATEGY_RATE_RANGE_KEY + key);
}
@Override
public String queryStrategyRuleValue(Long strategyId, String ruleModel) {
return queryStrategyRuleValue(strategyId, null, ruleModel);
}
@Override
public String queryStrategyRuleValue(Long strategyId, Integer awardId, String ruleModel) {
StrategyRule strategyRule = new StrategyRule();
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册