Merge branch 'master-jdk17' of https://gitee.com/zhijiantianya/yudao-cloud
commit
32c353c53d
|
|
@ -64,12 +64,10 @@ public class HttpUtils {
|
||||||
return URLDecoder.decode(encoded, StandardCharsets.UTF_8.name());
|
return URLDecoder.decode(encoded, StandardCharsets.UTF_8.name());
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public static String replaceUrlQuery(String url, String key, String value) {
|
public static String replaceUrlQuery(String url, String key, String value) {
|
||||||
UrlBuilder builder = UrlBuilder.of(url, Charset.defaultCharset());
|
UrlBuilder builder = UrlBuilder.of(url, Charset.defaultCharset());
|
||||||
// 先移除
|
// 先移除;再添加
|
||||||
builder.getQuery().remove(key);
|
builder.getQuery().remove(key);
|
||||||
// 后添加
|
|
||||||
builder.addQuery(key, value);
|
builder.addQuery(key, value);
|
||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,50 +1,52 @@
|
||||||
package cn.iocoder.yudao.framework.common.util.http;
|
package cn.iocoder.yudao.framework.common.util.http;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link HttpUtils} 的单元测试
|
||||||
|
*/
|
||||||
public class HttpUtilsTest {
|
public class HttpUtilsTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testReplaceUrlQuery() {
|
public void testReplaceUrlQuery_replace() {
|
||||||
// 定义测试用例:{原始URL, Key, Value, 期望结果}
|
// 准备参数
|
||||||
String[][] testCases = {
|
String url = "https://www.iocoder.cn/path?a=1&b=2";
|
||||||
// 场景1: 替换已存在的参数 (注意参数顺序可能会变,因为 UrlQuery 内部是 List)
|
// 调用
|
||||||
{"https://example.com/path?a=1&b=2", "a", "3", "https://example.com/path?b=2&a=3"},
|
String result = HttpUtils.replaceUrlQuery(url, "a", "3");
|
||||||
// 场景2: 添加不存在的参数
|
// 断言:被替换的 key 会移到末尾,原顺序的其它参数保留
|
||||||
{"https://example.com/path?a=1", "b", "2", "https://example.com/path?a=1&b=2"},
|
assertEquals("https://www.iocoder.cn/path?b=2&a=3", result);
|
||||||
// 场景3: URL 本身没有查询参数
|
|
||||||
{"https://example.com/path", "a", "1", "https://example.com/path?a=1"},
|
|
||||||
// 场景4: 值为空 (根据原逻辑,空值通常会被移除或不添加,这里假设是移除)
|
|
||||||
// 注意:你需要根据 HttpUtils 实际对 null/empty 的处理来调整 expected
|
|
||||||
{"https://example.com/path?a=1", "a", "", "https://example.com/path?a="},
|
|
||||||
};
|
|
||||||
|
|
||||||
System.out.println("开始运行 HttpUtils.replaceUrlQuery 测试...");
|
|
||||||
|
|
||||||
for (int i = 0; i < testCases.length; i++) {
|
|
||||||
String[] currentCase = testCases[i]; // 必须先取出当前这一行的数组
|
|
||||||
|
|
||||||
String url = currentCase[0];
|
|
||||||
String key = currentCase[1];
|
|
||||||
String value = currentCase[2];
|
|
||||||
String expected = currentCase[3];
|
|
||||||
|
|
||||||
// 调用你优化后的方法
|
|
||||||
String actual = HttpUtils.replaceUrlQuery(url, key, value);
|
|
||||||
|
|
||||||
// 核心验证:断言实际结果必须等于期望结果
|
|
||||||
// 如果不相等,测试会直接报错,并打印出是哪一行错了
|
|
||||||
try {
|
|
||||||
assertEquals(expected, actual, "测试用例 " + (i + 1) + " 失败: " + url);
|
|
||||||
System.out.println("✅ 用例 " + (i + 1) + " 通过: " + actual);
|
|
||||||
} catch (AssertionError e) {
|
|
||||||
System.err.println("❌ 用例 " + (i + 1) + " 失败!");
|
|
||||||
System.err.println(" 输入: " + url + " | " + key + "=" + value);
|
|
||||||
System.err.println(" 期望: " + expected);
|
|
||||||
System.err.println(" 实际: " + actual);
|
|
||||||
throw e; // 抛出错误,让测试标记为失败
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReplaceUrlQuery_add() {
|
||||||
|
// 准备参数
|
||||||
|
String url = "https://www.iocoder.cn/path?a=1";
|
||||||
|
// 调用
|
||||||
|
String result = HttpUtils.replaceUrlQuery(url, "b", "2");
|
||||||
|
// 断言
|
||||||
|
assertEquals("https://www.iocoder.cn/path?a=1&b=2", result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReplaceUrlQuery_noQuery() {
|
||||||
|
// 准备参数:原 URL 没有 query
|
||||||
|
String url = "https://www.iocoder.cn/path";
|
||||||
|
// 调用
|
||||||
|
String result = HttpUtils.replaceUrlQuery(url, "a", "1");
|
||||||
|
// 断言
|
||||||
|
assertEquals("https://www.iocoder.cn/path?a=1", result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReplaceUrlQuery_emptyValue() {
|
||||||
|
// 准备参数:value 为空字符串
|
||||||
|
String url = "https://www.iocoder.cn/path?a=1";
|
||||||
|
// 调用
|
||||||
|
String result = HttpUtils.replaceUrlQuery(url, "a", "");
|
||||||
|
// 断言:保留 key,value 为空
|
||||||
|
assertEquals("https://www.iocoder.cn/path?a=", result);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -41,7 +41,7 @@ public class IotAlertConfigServiceImpl implements IotAlertConfigService {
|
||||||
public Long createAlertConfig(IotAlertConfigSaveReqVO createReqVO) {
|
public Long createAlertConfig(IotAlertConfigSaveReqVO createReqVO) {
|
||||||
// 校验关联数据是否存在
|
// 校验关联数据是否存在
|
||||||
sceneRuleService.validateSceneRuleList(createReqVO.getSceneRuleIds());
|
sceneRuleService.validateSceneRuleList(createReqVO.getSceneRuleIds());
|
||||||
adminUserApi.validateUserList(createReqVO.getReceiveUserIds());
|
adminUserApi.validateUserList(createReqVO.getReceiveUserIds()).checkError();
|
||||||
|
|
||||||
// 插入
|
// 插入
|
||||||
IotAlertConfigDO alertConfig = BeanUtils.toBean(createReqVO, IotAlertConfigDO.class);
|
IotAlertConfigDO alertConfig = BeanUtils.toBean(createReqVO, IotAlertConfigDO.class);
|
||||||
|
|
@ -55,7 +55,7 @@ public class IotAlertConfigServiceImpl implements IotAlertConfigService {
|
||||||
validateAlertConfigExists(updateReqVO.getId());
|
validateAlertConfigExists(updateReqVO.getId());
|
||||||
// 校验关联数据是否存在
|
// 校验关联数据是否存在
|
||||||
sceneRuleService.validateSceneRuleList(updateReqVO.getSceneRuleIds());
|
sceneRuleService.validateSceneRuleList(updateReqVO.getSceneRuleIds());
|
||||||
adminUserApi.validateUserList(updateReqVO.getReceiveUserIds());
|
adminUserApi.validateUserList(updateReqVO.getReceiveUserIds()).checkError();
|
||||||
|
|
||||||
// 更新
|
// 更新
|
||||||
IotAlertConfigDO updateObj = BeanUtils.toBean(updateReqVO, IotAlertConfigDO.class);
|
IotAlertConfigDO updateObj = BeanUtils.toBean(updateReqVO, IotAlertConfigDO.class);
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,7 @@ public class IotAlertTriggerSceneRuleAction implements IotSceneRuleAction {
|
||||||
break;
|
break;
|
||||||
case NOTIFY:
|
case NOTIFY:
|
||||||
notifyMessageSendApi.sendSingleMessageToAdmin(new NotifySendSingleToUserReqDTO().setUserId(userId)
|
notifyMessageSendApi.sendSingleMessageToAdmin(new NotifySendSingleToUserReqDTO().setUserId(userId)
|
||||||
.setTemplateCode(typeEnum.getTemplateCode()).setTemplateParams(templateParams));
|
.setTemplateCode(typeEnum.getTemplateCode()).setTemplateParams(templateParams)).checkError();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,6 @@ import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
|
|
||||||
// TODO huihui:单测需要补充
|
|
||||||
/**
|
/**
|
||||||
* 砍价活动的 {@link TradePriceCalculator} 实现类
|
* 砍价活动的 {@link TradePriceCalculator} 实现类
|
||||||
*
|
*
|
||||||
|
|
@ -29,12 +28,12 @@ public class TradeBargainActivityPriceCalculator implements TradePriceCalculator
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) {
|
public void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) {
|
||||||
// 1. 判断订单类型和是否具有拼团记录编号
|
// 1. 判断订单类型和是否具有砍价记录编号
|
||||||
if (ObjectUtil.notEqual(result.getType(), TradeOrderTypeEnum.BARGAIN.getType())) {
|
if (ObjectUtil.notEqual(result.getType(), TradeOrderTypeEnum.BARGAIN.getType())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Assert.isTrue(param.getItems().size() == 1, "砍价时,只允许选择一个商品");
|
Assert.isTrue(param.getItems().size() == 1, "砍价时,只允许选择一个商品");
|
||||||
Assert.isTrue(param.getItems().get(0).getCount() == 1, "砍价时,只允许选择一个商品");
|
Assert.isTrue(param.getItems().get(0).getCount() == 1, "砍价时,商品数量只允许为 1");
|
||||||
// 2. 校验是否可以参与砍价
|
// 2. 校验是否可以参与砍价
|
||||||
TradePriceCalculateRespBO.OrderItem orderItem = result.getItems().get(0);
|
TradePriceCalculateRespBO.OrderItem orderItem = result.getItems().get(0);
|
||||||
BargainValidateJoinRespDTO bargainActivity = bargainRecordApi.validateJoinBargain(
|
BargainValidateJoinRespDTO bargainActivity = bargainRecordApi.validateJoinBargain(
|
||||||
|
|
@ -44,7 +43,7 @@ public class TradeBargainActivityPriceCalculator implements TradePriceCalculator
|
||||||
Integer discountPrice = orderItem.getPayPrice() - bargainActivity.getBargainPrice() * orderItem.getCount();
|
Integer discountPrice = orderItem.getPayPrice() - bargainActivity.getBargainPrice() * orderItem.getCount();
|
||||||
// TODO 芋艿:极端情况,优惠金额为负数,需要处理
|
// TODO 芋艿:极端情况,优惠金额为负数,需要处理
|
||||||
TradePriceCalculatorHelper.addPromotion(result, orderItem,
|
TradePriceCalculatorHelper.addPromotion(result, orderItem,
|
||||||
param.getSeckillActivityId(), bargainActivity.getName(), PromotionTypeEnum.BARGAIN_ACTIVITY.getType(),
|
bargainActivity.getActivityId(), bargainActivity.getName(), PromotionTypeEnum.BARGAIN_ACTIVITY.getType(),
|
||||||
StrUtil.format("砍价活动:省 {} 元", TradePriceCalculatorHelper.formatPrice(discountPrice)),
|
StrUtil.format("砍价活动:省 {} 元", TradePriceCalculatorHelper.formatPrice(discountPrice)),
|
||||||
discountPrice);
|
discountPrice);
|
||||||
// 3.2 更新 SKU 优惠金额
|
// 3.2 更新 SKU 优惠金额
|
||||||
|
|
|
||||||
|
|
@ -213,7 +213,7 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator {
|
||||||
}
|
}
|
||||||
double totalChargeValue = getTotalChargeValue(orderItems, chargeMode);
|
double totalChargeValue = getTotalChargeValue(orderItems, chargeMode);
|
||||||
double totalPrice = TradePriceCalculatorHelper.calculateTotalPayPrice(orderItems);
|
double totalPrice = TradePriceCalculatorHelper.calculateTotalPayPrice(orderItems);
|
||||||
return totalChargeValue <= templateFree.getFreeCount() && totalPrice >= templateFree.getFreePrice();
|
return totalChargeValue >= templateFree.getFreeCount() && totalPrice >= templateFree.getFreePrice();
|
||||||
}
|
}
|
||||||
|
|
||||||
private double getTotalChargeValue(List<OrderItem> orderItems, Integer chargeMode) {
|
private double getTotalChargeValue(List<OrderItem> orderItems, Integer chargeMode) {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package cn.iocoder.yudao.module.trade.service.price.calculator;
|
package cn.iocoder.yudao.module.trade.service.price.calculator;
|
||||||
|
|
||||||
import cn.hutool.core.lang.Assert;
|
import cn.hutool.core.lang.Assert;
|
||||||
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||||
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
|
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
|
||||||
|
|
@ -61,7 +62,7 @@ public class TradePriceCalculatorHelper {
|
||||||
// spu 信息
|
// spu 信息
|
||||||
orderItem.setSpuName(spu.getName()).setCategoryId(spu.getCategoryId())
|
orderItem.setSpuName(spu.getName()).setCategoryId(spu.getCategoryId())
|
||||||
.setDeliveryTypes(spu.getDeliveryTypes()).setDeliveryTemplateId(spu.getDeliveryTemplateId())
|
.setDeliveryTypes(spu.getDeliveryTypes()).setDeliveryTemplateId(spu.getDeliveryTemplateId())
|
||||||
.setGivePoint(spu.getGiveIntegral()).setUsePoint(0);
|
.setGivePoint(ObjectUtil.defaultIfNull(spu.getGiveIntegral(), 0)).setUsePoint(0);
|
||||||
if (StrUtil.isBlank(orderItem.getPicUrl())) {
|
if (StrUtil.isBlank(orderItem.getPicUrl())) {
|
||||||
orderItem.setPicUrl(spu.getPicUrl());
|
orderItem.setPicUrl(spu.getPicUrl());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -217,7 +217,7 @@ public class MesQcIndicatorResultServiceImpl implements MesQcIndicatorResultServ
|
||||||
if (Objects.equals(resultType, MesQcResultValueTypeEnum.DICT.getType())) {
|
if (Objects.equals(resultType, MesQcResultValueTypeEnum.DICT.getType())) {
|
||||||
String dictType = indicator.getResultSpecification();
|
String dictType = indicator.getResultSpecification();
|
||||||
if (StrUtil.isNotBlank(dictType)) {
|
if (StrUtil.isNotBlank(dictType)) {
|
||||||
dictDataApi.validateDictDataList(dictType, Collections.singleton(item.getValue()));
|
dictDataApi.validateDictDataList(dictType, Collections.singleton(item.getValue())).checkError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (Objects.equals(resultType, MesQcResultValueTypeEnum.FILE.getType())
|
if (Objects.equals(resultType, MesQcResultValueTypeEnum.FILE.getType())
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
package cn.iocoder.yudao.module.system.job.token;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
|
||||||
|
import cn.iocoder.yudao.module.system.service.oauth2.OAuth2TokenService;
|
||||||
|
import com.xxl.job.core.context.XxlJobHelper;
|
||||||
|
import com.xxl.job.core.handler.annotation.XxlJob;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 物理删除过期 N 天的令牌的 Job
|
||||||
|
*
|
||||||
|
* @author preschooler
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@Slf4j
|
||||||
|
public class TokenCleanJob {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private OAuth2TokenService oauth2TokenService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清理过期(14)天的令牌
|
||||||
|
*/
|
||||||
|
private static final Integer JOB_CLEAN_RETAIN_DAY = 14;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 每次删除间隔的条数,如果值太高可能会造成数据库的压力过大
|
||||||
|
*/
|
||||||
|
private static final Integer DELETE_LIMIT = 100;
|
||||||
|
|
||||||
|
@XxlJob("tokenCleanJob")
|
||||||
|
@TenantIgnore
|
||||||
|
public void execute() {
|
||||||
|
Integer refreshCount = oauth2TokenService.cleanRefreshToken(JOB_CLEAN_RETAIN_DAY, DELETE_LIMIT);
|
||||||
|
log.info("[execute][定时执行清理刷新令牌数量 ({}) 个]", refreshCount);
|
||||||
|
Integer accessCount = oauth2TokenService.cleanAccessToken(JOB_CLEAN_RETAIN_DAY, DELETE_LIMIT);
|
||||||
|
log.info("[execute][定时执行清理访问令牌数量 ({}) 个]", accessCount);
|
||||||
|
XxlJobHelper.handleSuccess(
|
||||||
|
String.format("定时执行清理刷新令牌数量 %s 个,访问令牌数量 %s 个", refreshCount, accessCount));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue