From de4abc2f5f854ff935ae7d5b55cdd1402fae7496 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 10 May 2026 10:11:24 +0800 Subject: [PATCH] =?UTF-8?q?=E3=80=90=E5=90=8C=E6=AD=A5=E3=80=91BOOT=20?= =?UTF-8?q?=E5=92=8C=20CLOUD=20=E7=9A=84=E5=8A=9F=E8=83=BD=EF=BC=88mall=20?= =?UTF-8?q?+=20mes=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../framework/common/util/http/HttpUtils.java | 4 +- .../common/util/http/HttpUtilsTest.java | 84 ++++++++++--------- .../alert/IotAlertConfigServiceImpl.java | 4 +- .../IotAlertTriggerSceneRuleAction.java | 2 +- .../TradeBargainActivityPriceCalculator.java | 7 +- .../TradeDeliveryPriceCalculator.java | 2 +- .../TradePriceCalculatorHelper.java | 3 +- .../MesQcIndicatorResultServiceImpl.java | 2 +- .../system/job/token/TokenCleanJob.java | 44 ++++++++++ 9 files changed, 98 insertions(+), 54 deletions(-) create mode 100644 yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/job/token/TokenCleanJob.java diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/http/HttpUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/http/HttpUtils.java index a31f4820c..d27498185 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/http/HttpUtils.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/http/HttpUtils.java @@ -60,12 +60,10 @@ public class HttpUtils { return URLDecoder.decode(encoded, StandardCharsets.UTF_8); } - @SuppressWarnings("unchecked") public static String replaceUrlQuery(String url, String key, String value) { UrlBuilder builder = UrlBuilder.of(url, Charset.defaultCharset()); - // 先移除 + // 先移除;再添加 builder.getQuery().remove(key); - // 后添加 builder.addQuery(key, value); return builder.build(); } diff --git a/yudao-framework/yudao-common/src/test/java/cn/iocoder/yudao/framework/common/util/http/HttpUtilsTest.java b/yudao-framework/yudao-common/src/test/java/cn/iocoder/yudao/framework/common/util/http/HttpUtilsTest.java index 861598a56..a42874f04 100644 --- a/yudao-framework/yudao-common/src/test/java/cn/iocoder/yudao/framework/common/util/http/HttpUtilsTest.java +++ b/yudao-framework/yudao-common/src/test/java/cn/iocoder/yudao/framework/common/util/http/HttpUtilsTest.java @@ -1,50 +1,52 @@ package cn.iocoder.yudao.framework.common.util.http; import org.junit.jupiter.api.Test; + import static org.junit.jupiter.api.Assertions.assertEquals; +/** + * {@link HttpUtils} 的单元测试 + */ public class HttpUtilsTest { @Test - public void testReplaceUrlQuery() { - // 定义测试用例:{原始URL, Key, Value, 期望结果} - String[][] testCases = { - // 场景1: 替换已存在的参数 (注意参数顺序可能会变,因为 UrlQuery 内部是 List) - {"https://example.com/path?a=1&b=2", "a", "3", "https://example.com/path?b=2&a=3"}, - // 场景2: 添加不存在的参数 - {"https://example.com/path?a=1", "b", "2", "https://example.com/path?a=1&b=2"}, - // 场景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; // 抛出错误,让测试标记为失败 - } - } + public void testReplaceUrlQuery_replace() { + // 准备参数 + String url = "https://www.iocoder.cn/path?a=1&b=2"; + // 调用 + String result = HttpUtils.replaceUrlQuery(url, "a", "3"); + // 断言:被替换的 key 会移到末尾,原顺序的其它参数保留 + assertEquals("https://www.iocoder.cn/path?b=2&a=3", result); } -} \ No newline at end of file + + @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); + } + +} diff --git a/yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/alert/IotAlertConfigServiceImpl.java b/yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/alert/IotAlertConfigServiceImpl.java index aa9378767..77be87fb8 100644 --- a/yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/alert/IotAlertConfigServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/alert/IotAlertConfigServiceImpl.java @@ -41,7 +41,7 @@ public class IotAlertConfigServiceImpl implements IotAlertConfigService { public Long createAlertConfig(IotAlertConfigSaveReqVO createReqVO) { // 校验关联数据是否存在 sceneRuleService.validateSceneRuleList(createReqVO.getSceneRuleIds()); - adminUserApi.validateUserList(createReqVO.getReceiveUserIds()); + adminUserApi.validateUserList(createReqVO.getReceiveUserIds()).checkError(); // 插入 IotAlertConfigDO alertConfig = BeanUtils.toBean(createReqVO, IotAlertConfigDO.class); @@ -55,7 +55,7 @@ public class IotAlertConfigServiceImpl implements IotAlertConfigService { validateAlertConfigExists(updateReqVO.getId()); // 校验关联数据是否存在 sceneRuleService.validateSceneRuleList(updateReqVO.getSceneRuleIds()); - adminUserApi.validateUserList(updateReqVO.getReceiveUserIds()); + adminUserApi.validateUserList(updateReqVO.getReceiveUserIds()).checkError(); // 更新 IotAlertConfigDO updateObj = BeanUtils.toBean(updateReqVO, IotAlertConfigDO.class); diff --git a/yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/IotAlertTriggerSceneRuleAction.java b/yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/IotAlertTriggerSceneRuleAction.java index bc264366f..d5656b970 100644 --- a/yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/IotAlertTriggerSceneRuleAction.java +++ b/yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/IotAlertTriggerSceneRuleAction.java @@ -102,7 +102,7 @@ public class IotAlertTriggerSceneRuleAction implements IotSceneRuleAction { break; case NOTIFY: notifyMessageSendApi.sendSingleMessageToAdmin(new NotifySendSingleToUserReqDTO().setUserId(userId) - .setTemplateCode(typeEnum.getTemplateCode()).setTemplateParams(templateParams)); + .setTemplateCode(typeEnum.getTemplateCode()).setTemplateParams(templateParams)).checkError(); break; } } catch (Exception ex) { diff --git a/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeBargainActivityPriceCalculator.java b/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeBargainActivityPriceCalculator.java index ca1c08d04..b3d276342 100644 --- a/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeBargainActivityPriceCalculator.java +++ b/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeBargainActivityPriceCalculator.java @@ -14,7 +14,6 @@ import org.springframework.stereotype.Component; import jakarta.annotation.Resource; -// TODO huihui:单测需要补充 /** * 砍价活动的 {@link TradePriceCalculator} 实现类 * @@ -29,12 +28,12 @@ public class TradeBargainActivityPriceCalculator implements TradePriceCalculator @Override public void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) { - // 1. 判断订单类型和是否具有拼团记录编号 + // 1. 判断订单类型和是否具有砍价记录编号 if (ObjectUtil.notEqual(result.getType(), TradeOrderTypeEnum.BARGAIN.getType())) { return; } Assert.isTrue(param.getItems().size() == 1, "砍价时,只允许选择一个商品"); - Assert.isTrue(param.getItems().get(0).getCount() == 1, "砍价时,只允许选择一个商品"); + Assert.isTrue(param.getItems().get(0).getCount() == 1, "砍价时,商品数量只允许为 1"); // 2. 校验是否可以参与砍价 TradePriceCalculateRespBO.OrderItem orderItem = result.getItems().get(0); BargainValidateJoinRespDTO bargainActivity = bargainRecordApi.validateJoinBargain( @@ -44,7 +43,7 @@ public class TradeBargainActivityPriceCalculator implements TradePriceCalculator Integer discountPrice = orderItem.getPayPrice() - bargainActivity.getBargainPrice() * orderItem.getCount(); // TODO 芋艿:极端情况,优惠金额为负数,需要处理 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)), discountPrice); // 3.2 更新 SKU 优惠金额 diff --git a/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeDeliveryPriceCalculator.java b/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeDeliveryPriceCalculator.java index 200b8e5cd..f95880aa4 100644 --- a/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeDeliveryPriceCalculator.java +++ b/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeDeliveryPriceCalculator.java @@ -213,7 +213,7 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator { } double totalChargeValue = getTotalChargeValue(orderItems, chargeMode); double totalPrice = TradePriceCalculatorHelper.calculateTotalPayPrice(orderItems); - return totalChargeValue <= templateFree.getFreeCount() && totalPrice >= templateFree.getFreePrice(); + return totalChargeValue >= templateFree.getFreeCount() && totalPrice >= templateFree.getFreePrice(); } private double getTotalChargeValue(List orderItems, Integer chargeMode) { diff --git a/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePriceCalculatorHelper.java b/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePriceCalculatorHelper.java index 6585c9da2..b74c8ba3f 100644 --- a/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePriceCalculatorHelper.java +++ b/yudao-module-mall/yudao-module-trade-server/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePriceCalculatorHelper.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.trade.service.price.calculator; import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO; @@ -61,7 +62,7 @@ public class TradePriceCalculatorHelper { // spu 信息 orderItem.setSpuName(spu.getName()).setCategoryId(spu.getCategoryId()) .setDeliveryTypes(spu.getDeliveryTypes()).setDeliveryTemplateId(spu.getDeliveryTemplateId()) - .setGivePoint(spu.getGiveIntegral()).setUsePoint(0); + .setGivePoint(ObjectUtil.defaultIfNull(spu.getGiveIntegral(), 0)).setUsePoint(0); if (StrUtil.isBlank(orderItem.getPicUrl())) { orderItem.setPicUrl(spu.getPicUrl()); } diff --git a/yudao-module-mes/yudao-module-mes-server/src/main/java/cn/iocoder/yudao/module/mes/service/qc/indicatorresult/MesQcIndicatorResultServiceImpl.java b/yudao-module-mes/yudao-module-mes-server/src/main/java/cn/iocoder/yudao/module/mes/service/qc/indicatorresult/MesQcIndicatorResultServiceImpl.java index c6b162f44..99fe7f456 100644 --- a/yudao-module-mes/yudao-module-mes-server/src/main/java/cn/iocoder/yudao/module/mes/service/qc/indicatorresult/MesQcIndicatorResultServiceImpl.java +++ b/yudao-module-mes/yudao-module-mes-server/src/main/java/cn/iocoder/yudao/module/mes/service/qc/indicatorresult/MesQcIndicatorResultServiceImpl.java @@ -217,7 +217,7 @@ public class MesQcIndicatorResultServiceImpl implements MesQcIndicatorResultServ if (Objects.equals(resultType, MesQcResultValueTypeEnum.DICT.getType())) { String dictType = indicator.getResultSpecification(); 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()) diff --git a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/job/token/TokenCleanJob.java b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/job/token/TokenCleanJob.java new file mode 100644 index 000000000..e55d0933f --- /dev/null +++ b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/job/token/TokenCleanJob.java @@ -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)); + } + +}