6.28 动态配置活动降级拦截及用户切量拦截
This commit is contained in:
parent
8ff266ff41
commit
aa1002d74c
@ -0,0 +1,12 @@
|
|||||||
|
package edu.whut.api;
|
||||||
|
|
||||||
|
import edu.whut.api.response.Response;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DCC 动态配置中心
|
||||||
|
*/
|
||||||
|
public interface IDCCService {
|
||||||
|
|
||||||
|
Response<Boolean> updateConfig(String key, String value);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,160 @@
|
|||||||
|
package edu.whut.config;
|
||||||
|
|
||||||
|
import edu.whut.types.annotations.DCCValue;
|
||||||
|
import edu.whut.types.common.Constants;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.redisson.api.RBucket;
|
||||||
|
import org.redisson.api.RTopic;
|
||||||
|
import org.redisson.api.RedissonClient;
|
||||||
|
import org.springframework.aop.framework.AopProxyUtils;
|
||||||
|
import org.springframework.aop.support.AopUtils;
|
||||||
|
import org.springframework.beans.BeansException;
|
||||||
|
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dynamic Config Center(DCC)—— 基于 Redis(Redisson)实现的轻量级配置中心。
|
||||||
|
* 作用:
|
||||||
|
* 1. Bean 初始化阶段扫描带 {@link DCCValue} 注解的字段,将默认值写入 Redis 并注入到 Bean;
|
||||||
|
* 2. 通过 Redis Topic 监听配置变更事件,实现运行期热更新;
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Configuration
|
||||||
|
public class DCCValueBeanFactory implements BeanPostProcessor {
|
||||||
|
|
||||||
|
/** Redis 中所有 DCC Key 的统一前缀 */
|
||||||
|
private static final String BASE_CONFIG_PATH = "group_buy_market_dcc_";
|
||||||
|
|
||||||
|
private final RedissonClient redissonClient;
|
||||||
|
|
||||||
|
/** 记录「配置 Key → 注入了该 Key 的 Bean 实例」的映射,用于收到变更后反射刷新字段值 */
|
||||||
|
private final Map<String, Object> dccObjGroup = new HashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过构造器注入 RedissonClient,便于单元测试 Mock。
|
||||||
|
* @param redissonClient Spring 上下文提供的 RedissonClient
|
||||||
|
*/
|
||||||
|
public DCCValueBeanFactory(RedissonClient redissonClient) {
|
||||||
|
this.redissonClient = redissonClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 定义一个 {@link RTopic} Bean 并注册监听器,用于接收
|
||||||
|
* “其他节点发布的配置变更消息”。
|
||||||
|
* 消息格式:attribute:value,例如 cutRange:30。
|
||||||
|
* @param redissonClient 容器注入的 RedissonClient
|
||||||
|
* @return 配置变更 Topic 对象(Bean 名称为 dccTopic
|
||||||
|
*/
|
||||||
|
@Bean("dccTopic")
|
||||||
|
public RTopic dccRedisTopicListener(RedissonClient redissonClient) {
|
||||||
|
RTopic topic = redissonClient.getTopic("group_buy_market_dcc");
|
||||||
|
|
||||||
|
topic.addListener(String.class, (channel, message) -> {
|
||||||
|
// message 示例: "cutRange:30"
|
||||||
|
String[] split = message.split(Constants.SPLIT);
|
||||||
|
String attribute = split[0]; // 字段名
|
||||||
|
String value = split[1]; // 新值
|
||||||
|
|
||||||
|
String key = BASE_CONFIG_PATH + attribute;
|
||||||
|
|
||||||
|
// 1. 写回 Redis,保证一致性
|
||||||
|
RBucket<String> bucket = redissonClient.getBucket(key);
|
||||||
|
if (!bucket.isExists()) { return; }
|
||||||
|
bucket.set(value);
|
||||||
|
|
||||||
|
// 2. 本地内存刷新:反射写回所有注入了该字段的 Bean
|
||||||
|
Object objBean = dccObjGroup.get(key);
|
||||||
|
if (objBean == null) { return; }
|
||||||
|
|
||||||
|
Class<?> objBeanClass = objBean.getClass();
|
||||||
|
// 兼容 AOP 代理场景:取真实目标类
|
||||||
|
if (AopUtils.isAopProxy(objBean)) {
|
||||||
|
objBeanClass = AopUtils.getTargetClass(objBean);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Field field = objBeanClass.getDeclaredField(attribute);
|
||||||
|
field.setAccessible(true);
|
||||||
|
field.set(objBean, value);
|
||||||
|
field.setAccessible(false);
|
||||||
|
|
||||||
|
log.info("DCC → 热更新成功:{} = {}", key, value);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("DCC 反射刷新失败:" + key, e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return topic;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spring 容器在启动并创建每个 Bean 的时候,都会依次回调postProcessAfterInitialization 方法:
|
||||||
|
* 扫描所有带 {@link DCCValue} 注解的字段;
|
||||||
|
* 把默认值写入 Redis(若不存在);
|
||||||
|
* 把 Redis 中最新值注入到字段;
|
||||||
|
* 记录「key → Bean 实例」映射以便后续热更新。
|
||||||
|
*
|
||||||
|
* @param bean 当前实例化完成的 Bean
|
||||||
|
* @param beanName Spring 为该 Bean 生成的名称
|
||||||
|
* @return 可能被改写后的 Bean(此处直接返回原 Bean)
|
||||||
|
* @throws BeansException Spring 容器异常
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
|
||||||
|
|
||||||
|
// 处理 AOP 代理:拿到目标类 & 真实对象
|
||||||
|
//默认:假设 bean 就是我们想要的目标类
|
||||||
|
Class<?> targetBeanClass = bean.getClass();
|
||||||
|
Object targetBeanObj = bean;
|
||||||
|
//如果它是一个 AOP 代理对象,就“剥掉代理”,找到真实的类和实例
|
||||||
|
if (AopUtils.isAopProxy(bean)) {
|
||||||
|
targetBeanClass = AopUtils.getTargetClass(bean);
|
||||||
|
targetBeanObj = AopProxyUtils.getSingletonTarget(bean);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 遍历字段
|
||||||
|
for (Field field : targetBeanClass.getDeclaredFields()) {
|
||||||
|
//寻找带 @DCCValue 的字段
|
||||||
|
if (!field.isAnnotationPresent(DCCValue.class)) { continue; }
|
||||||
|
|
||||||
|
DCCValue dccValue = field.getAnnotation(DCCValue.class);
|
||||||
|
String rawValue = dccValue.value(); // eg:"downgradeSwitch:0"
|
||||||
|
if (StringUtils.isBlank(rawValue)) {
|
||||||
|
throw new RuntimeException(field.getName() + " 缺少 @DCCValue 默认值");
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] splits = rawValue.split(":");
|
||||||
|
String key = BASE_CONFIG_PATH + splits[0];
|
||||||
|
String defaultVal = splits.length == 2 ? splits[1] : null;
|
||||||
|
if (StringUtils.isBlank(defaultVal)) {
|
||||||
|
throw new RuntimeException("DCC Key " + key + " 未配置默认值");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. Redis 同步:若不存在则写默认值;若已存在则取最新值
|
||||||
|
RBucket<String> bucket = redissonClient.getBucket(key);
|
||||||
|
String injectedValue = bucket.isExists() ? bucket.get() : defaultVal;
|
||||||
|
if (!bucket.isExists()) { bucket.set(defaultVal); }
|
||||||
|
|
||||||
|
// 2. 反射注入字段
|
||||||
|
try {
|
||||||
|
field.setAccessible(true);
|
||||||
|
field.set(targetBeanObj, injectedValue);
|
||||||
|
field.setAccessible(false);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException("DCC 注入字段失败:" + key, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 记录「key → Bean」,供 Topic 监听时热更新
|
||||||
|
dccObjGroup.put(key, targetBeanObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
return bean;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,55 @@
|
|||||||
|
package edu.whut.test.trigger;
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
|
import edu.whut.api.IDCCService;
|
||||||
|
import edu.whut.domain.activity.model.entity.MarketProductEntity;
|
||||||
|
import edu.whut.domain.activity.model.entity.TrialBalanceEntity;
|
||||||
|
import edu.whut.domain.activity.service.IIndexGroupBuyMarketService;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.test.context.junit4.SpringRunner;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 动态配置管理测试
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@RunWith(SpringRunner.class)
|
||||||
|
@SpringBootTest
|
||||||
|
public class DCCControllerTest {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private IDCCService dccService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private IIndexGroupBuyMarketService indexGroupBuyMarketService;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_updateConfig() {
|
||||||
|
// 动态调整配置
|
||||||
|
dccService.updateConfig("downgradeSwitch", "1");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_updateConfig2indexMarketTrial() throws Exception {
|
||||||
|
// 动态调整配置
|
||||||
|
dccService.updateConfig("downgradeSwitch", "1");
|
||||||
|
// 超时等待异步
|
||||||
|
Thread.sleep(1000);
|
||||||
|
|
||||||
|
// 营销验证
|
||||||
|
MarketProductEntity marketProductEntity = new MarketProductEntity();
|
||||||
|
marketProductEntity.setUserId("smile");
|
||||||
|
marketProductEntity.setSource("s01");
|
||||||
|
marketProductEntity.setChannel("c01");
|
||||||
|
marketProductEntity.setGoodsId("9890001");
|
||||||
|
|
||||||
|
TrialBalanceEntity trialBalanceEntity = indexGroupBuyMarketService.indexMarketTrial(marketProductEntity);
|
||||||
|
log.info("请求参数:{}", JSON.toJSONString(marketProductEntity));
|
||||||
|
log.info("返回结果:{}", JSON.toJSONString(trialBalanceEntity));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -16,4 +16,8 @@ public interface IActivityRepository {
|
|||||||
|
|
||||||
boolean isTagCrowdRange(String tagId, String userId);
|
boolean isTagCrowdRange(String tagId, String userId);
|
||||||
|
|
||||||
|
boolean downgradeSwitch();
|
||||||
|
|
||||||
|
boolean cutRange(String userId);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -31,8 +31,6 @@ public class MarketNode extends AbstractGroupBuyMarketSupport<MarketProductEntit
|
|||||||
|
|
||||||
private final ThreadPoolExecutor threadPoolExecutor;
|
private final ThreadPoolExecutor threadPoolExecutor;
|
||||||
|
|
||||||
private final EndNode endNode;
|
|
||||||
|
|
||||||
private final ErrorNode errorNode;
|
private final ErrorNode errorNode;
|
||||||
|
|
||||||
private final Map<String, IDiscountCalculateService> discountCalculateServiceMap;
|
private final Map<String, IDiscountCalculateService> discountCalculateServiceMap;
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
package edu.whut.domain.activity.service.trial.node;
|
package edu.whut.domain.activity.service.trial.node;
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
import edu.whut.domain.activity.model.entity.MarketProductEntity;
|
import edu.whut.domain.activity.model.entity.MarketProductEntity;
|
||||||
import edu.whut.domain.activity.model.entity.TrialBalanceEntity;
|
import edu.whut.domain.activity.model.entity.TrialBalanceEntity;
|
||||||
import edu.whut.domain.activity.service.trial.AbstractGroupBuyMarketSupport;
|
import edu.whut.domain.activity.service.trial.AbstractGroupBuyMarketSupport;
|
||||||
import edu.whut.domain.activity.service.trial.factory.DefaultActivityStrategyFactory;
|
import edu.whut.domain.activity.service.trial.factory.DefaultActivityStrategyFactory;
|
||||||
import edu.whut.types.design.framework.tree.StrategyHandler;
|
import edu.whut.types.design.framework.tree.StrategyHandler;
|
||||||
|
import edu.whut.types.enums.ResponseCode;
|
||||||
|
import edu.whut.types.exception.AppException;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
@ -20,6 +23,22 @@ public class SwitchNode extends AbstractGroupBuyMarketSupport<MarketProductEntit
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TrialBalanceEntity doApply(MarketProductEntity requestParameter, DefaultActivityStrategyFactory.DynamicContext dynamicContext) throws Exception {
|
public TrialBalanceEntity doApply(MarketProductEntity requestParameter, DefaultActivityStrategyFactory.DynamicContext dynamicContext) throws Exception {
|
||||||
|
log.info("拼团商品查询试算服务-SwitchNode userId:{} requestParameter:{}", requestParameter.getUserId(), JSON.toJSONString(requestParameter));
|
||||||
|
|
||||||
|
// 根据用户ID切量
|
||||||
|
String userId = requestParameter.getUserId();
|
||||||
|
|
||||||
|
// 判断是否降级
|
||||||
|
if (repository.downgradeSwitch()) {
|
||||||
|
log.info("拼团活动降级拦截 {}", userId);
|
||||||
|
throw new AppException(ResponseCode.E0003.getCode(), ResponseCode.E0003.getInfo());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 切量范围判断
|
||||||
|
if (!repository.cutRange(userId)) {
|
||||||
|
log.info("拼团活动切量拦截 {}", userId);
|
||||||
|
throw new AppException(ResponseCode.E0004.getCode(), ResponseCode.E0004.getInfo());
|
||||||
|
}
|
||||||
return router(requestParameter, dynamicContext);
|
return router(requestParameter, dynamicContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ import edu.whut.infrastructure.dao.IGroupBuyActivityDao;
|
|||||||
import edu.whut.infrastructure.dao.IGroupBuyDiscountDao;
|
import edu.whut.infrastructure.dao.IGroupBuyDiscountDao;
|
||||||
import edu.whut.infrastructure.dao.ISCSkuActivityDao;
|
import edu.whut.infrastructure.dao.ISCSkuActivityDao;
|
||||||
import edu.whut.infrastructure.dao.ISkuDao;
|
import edu.whut.infrastructure.dao.ISkuDao;
|
||||||
|
import edu.whut.infrastructure.dao.dcc.DCCService;
|
||||||
import edu.whut.infrastructure.dao.po.GroupBuyActivity;
|
import edu.whut.infrastructure.dao.po.GroupBuyActivity;
|
||||||
import edu.whut.infrastructure.dao.po.GroupBuyDiscount;
|
import edu.whut.infrastructure.dao.po.GroupBuyDiscount;
|
||||||
import edu.whut.infrastructure.dao.po.SCSkuActivity;
|
import edu.whut.infrastructure.dao.po.SCSkuActivity;
|
||||||
@ -19,7 +20,6 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import org.redisson.api.RBitSet;
|
import org.redisson.api.RBitSet;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 活动仓储
|
* 活动仓储
|
||||||
@ -39,6 +39,8 @@ public class ActivityRepository implements IActivityRepository {
|
|||||||
|
|
||||||
private final IRedisService redisService;
|
private final IRedisService redisService;
|
||||||
|
|
||||||
|
private final DCCService dccService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GroupBuyActivityDiscountVO queryGroupBuyActivityDiscountVO(Long activityId) {
|
public GroupBuyActivityDiscountVO queryGroupBuyActivityDiscountVO(Long activityId) {
|
||||||
GroupBuyActivity groupBuyActivityRes = groupBuyActivityDao.queryValidGroupBuyActivityId(activityId);
|
GroupBuyActivity groupBuyActivityRes = groupBuyActivityDao.queryValidGroupBuyActivityId(activityId);
|
||||||
@ -114,4 +116,14 @@ public class ActivityRepository implements IActivityRepository {
|
|||||||
return bitSet.get(redisService.getIndexFromUserId(userId));
|
return bitSet.get(redisService.getIndexFromUserId(userId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean downgradeSwitch() {
|
||||||
|
return dccService.isDowngradeSwitch();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean cutRange(String userId) {
|
||||||
|
return dccService.isCutRange(userId);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,46 @@
|
|||||||
|
package edu.whut.infrastructure.dao.dcc;
|
||||||
|
import edu.whut.types.annotations.DCCValue;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DCC动态配置服务 Dynamic Configuration Center
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class DCCService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 降级开关 0关闭、1开启 当外部依赖异常、系统负载过高等场景下,主动关闭或简化某些功能
|
||||||
|
*/
|
||||||
|
@DCCValue("downgradeSwitch:0")
|
||||||
|
private String downgradeSwitch;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 人群切量开关,只让部分人群先使用新功能或新版本
|
||||||
|
*/
|
||||||
|
@DCCValue("cutRange:100")
|
||||||
|
private String cutRange;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否降级
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public boolean isDowngradeSwitch() {
|
||||||
|
return "1".equals(downgradeSwitch);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCutRange(String userId) {
|
||||||
|
// 计算哈希码的绝对值
|
||||||
|
int hashCode = Math.abs(userId.hashCode());
|
||||||
|
|
||||||
|
// 获取最后两位
|
||||||
|
int lastTwoDigits = hashCode % 100;
|
||||||
|
|
||||||
|
// 判断是否在切量范围内
|
||||||
|
if (lastTwoDigits <= Integer.parseInt(cutRange)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -26,7 +26,10 @@
|
|||||||
<groupId>org.apache.commons</groupId>
|
<groupId>org.apache.commons</groupId>
|
||||||
<artifactId>commons-lang3</artifactId>
|
<artifactId>commons-lang3</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.redisson</groupId>
|
||||||
|
<artifactId>redisson-spring-boot-starter</artifactId>
|
||||||
|
</dependency>
|
||||||
<!-- 系统模块 -->
|
<!-- 系统模块 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>edu.whut</groupId>
|
<groupId>edu.whut</groupId>
|
||||||
|
@ -0,0 +1,49 @@
|
|||||||
|
package edu.whut.trigger.http;
|
||||||
|
import edu.whut.api.IDCCService;
|
||||||
|
import edu.whut.api.response.Response;
|
||||||
|
import edu.whut.types.enums.ResponseCode;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.redisson.api.RTopic;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 动态配置管理
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@RestController()
|
||||||
|
@CrossOrigin("*")
|
||||||
|
@RequestMapping("/api/v1/gbm/dcc/")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class DCCController implements IDCCService {
|
||||||
|
|
||||||
|
private final RTopic dccTopic;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 动态值变更
|
||||||
|
* curl http://localhost:8091/api/v1/gbm/dcc/update_config?key=downgradeSwitch&value=1
|
||||||
|
* curl http://localhost:8091/api/v1/gbm/dcc/update_config?key=cutRange&value=0
|
||||||
|
*/
|
||||||
|
@GetMapping("update_config")
|
||||||
|
@Override
|
||||||
|
public Response<Boolean> updateConfig(@RequestParam String key,
|
||||||
|
@RequestParam String value) {
|
||||||
|
try {
|
||||||
|
log.info("DCC 动态配置值变更 key:{} value:{}", key, value);
|
||||||
|
dccTopic.publish(key + "," + value);
|
||||||
|
return Response.<Boolean>builder()
|
||||||
|
.code(ResponseCode.SUCCESS.getCode())
|
||||||
|
.info(ResponseCode.SUCCESS.getInfo())
|
||||||
|
.build();
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("DCC 动态配置值变更失败 key:{} value:{}", key, value, e);
|
||||||
|
return Response.<Boolean>builder()
|
||||||
|
.code(ResponseCode.UN_ERROR.getCode())
|
||||||
|
.info(ResponseCode.UN_ERROR.getInfo())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package edu.whut.types.annotations;
|
||||||
|
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注解,动态配置中心标记
|
||||||
|
*/
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target({ElementType.FIELD})
|
||||||
|
@Documented
|
||||||
|
public @interface DCCValue {
|
||||||
|
|
||||||
|
String value() default "";
|
||||||
|
|
||||||
|
}
|
@ -14,6 +14,8 @@ public enum ResponseCode {
|
|||||||
ILLEGAL_PARAMETER("0002", "非法参数"),
|
ILLEGAL_PARAMETER("0002", "非法参数"),
|
||||||
E0001("E0001", "不存在对应的折扣计算服务"),
|
E0001("E0001", "不存在对应的折扣计算服务"),
|
||||||
E0002("E0002", "无拼团营销配置"),
|
E0002("E0002", "无拼团营销配置"),
|
||||||
|
E0003("E0003", "拼团活动降级拦截"),
|
||||||
|
E0004("E0004", "拼团活动切量拦截"),
|
||||||
;
|
;
|
||||||
|
|
||||||
private String code;
|
private String code;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user