From 17943589a4c9af60b5fc61f06060cf3dffd72c09 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 23 May 2026 16:20:19 +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=88?= =?UTF-8?q?=E6=A1=86=E6=9E=B6=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yudao-dependencies/pom.xml | 19 ++++ .../yudao-spring-boot-starter-mybatis/pom.xml | 7 ++ .../mybatis/core/util/MyBatisUtils.java | 46 ++++++-- .../mybatis/core/util/MyBatisUtilsTest.java | 106 ++++++++++++++++++ .../core/filter/ApiAccessLogFilter.java | 2 +- .../interceptor/ApiAccessLogInterceptor.java | 2 +- .../mapper/brokerage/BrokerageUserMapper.xml | 27 +++-- .../admin/user/MemberUserController.java | 2 +- 8 files changed, 192 insertions(+), 19 deletions(-) create mode 100644 yudao-framework/yudao-spring-boot-starter-mybatis/src/test/java/cn/iocoder/yudao/framework/mybatis/core/util/MyBatisUtilsTest.java diff --git a/yudao-dependencies/pom.xml b/yudao-dependencies/pom.xml index 78a0ca658..99430571f 100644 --- a/yudao-dependencies/pom.xml +++ b/yudao-dependencies/pom.xml @@ -85,6 +85,7 @@ 2.3.2 2.3.2 4.8.2-20260501.180637 + 1.80 4.40.771.ALL @@ -689,6 +690,24 @@ + + + org.bouncycastle + bcprov-jdk18on + ${bouncycastle.version} + + + org.bouncycastle + bcutil-jdk18on + ${bouncycastle.version} + + + org.bouncycastle + bcpkix-jdk18on + ${bouncycastle.version} + + com.github.binarywang weixin-java-pay diff --git a/yudao-framework/yudao-spring-boot-starter-mybatis/pom.xml b/yudao-framework/yudao-spring-boot-starter-mybatis/pom.xml index 8afabd9c5..a54adfbc3 100644 --- a/yudao-framework/yudao-spring-boot-starter-mybatis/pom.xml +++ b/yudao-framework/yudao-spring-boot-starter-mybatis/pom.xml @@ -100,6 +100,13 @@ com.fhs-opensource easy-trans-mybatis-plus-extend + + + + org.springframework.boot + spring-boot-starter-test + test + diff --git a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/util/MyBatisUtils.java b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/util/MyBatisUtils.java index e9d400aaa..a23930187 100644 --- a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/util/MyBatisUtils.java +++ b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/util/MyBatisUtils.java @@ -23,6 +23,7 @@ import net.sf.jsqlparser.schema.Table; import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.regex.Pattern; /** * MyBatis 工具类 @@ -31,6 +32,8 @@ public class MyBatisUtils { private static final String MYSQL_ESCAPE_CHARACTER = "`"; + private static final Pattern SAFE_COLUMN_NAME_PATTERN = Pattern.compile("^[a-zA-Z0-9_]+(\\.[a-zA-Z0-9_]+)*$"); + public static Page buildPage(PageParam pageParam) { return buildPage(pageParam, null); } @@ -42,8 +45,11 @@ public class MyBatisUtils { // 排序字段 if (CollUtil.isNotEmpty(sortingFields)) { for (SortingField sortingField : sortingFields) { - page.addOrder(new OrderItem().setAsc(SortingField.ORDER_ASC.equals(sortingField.getOrder())) - .setColumn(StrUtil.toUnderlineCase(sortingField.getField()))); + String columnName = buildSafeOrderColumn(sortingField.getField()); + if (columnName == null) { + continue; + } + page.addOrder(new OrderItem().setAsc(isAscOrder(sortingField.getOrder())).setColumn(columnName)); } } return page; @@ -57,23 +63,29 @@ public class MyBatisUtils { if (wrapper instanceof QueryWrapper) { QueryWrapper query = (QueryWrapper) wrapper; for (SortingField sortingField : sortingFields) { - query.orderBy(true, - SortingField.ORDER_ASC.equals(sortingField.getOrder()), - StrUtil.toUnderlineCase(sortingField.getField())); + String columnName = buildSafeOrderColumn(sortingField.getField()); + if (columnName == null) { + continue; + } + query.orderBy(true, isAscOrder(sortingField.getOrder()), columnName); } } else if (wrapper instanceof LambdaQueryWrapper) { // LambdaQueryWrapper 不直接支持字符串字段排序,使用 last 方法拼接 ORDER BY LambdaQueryWrapper lambdaQuery = (LambdaQueryWrapper) wrapper; StringBuilder orderBy = new StringBuilder(); for (SortingField sortingField : sortingFields) { + String columnName = buildSafeOrderColumn(sortingField.getField()); + if (columnName == null) { + continue; + } if (StrUtil.isNotEmpty(orderBy)) { orderBy.append(", "); } - orderBy.append(StrUtil.toUnderlineCase(sortingField.getField())) - .append(" ") - .append(SortingField.ORDER_ASC.equals(sortingField.getOrder()) ? "ASC" : "DESC"); + orderBy.append(columnName).append(" ").append(getOrderDirection(sortingField.getOrder())); + } + if (StrUtil.isNotEmpty(orderBy)) { + lambdaQuery.last("ORDER BY " + orderBy); } - lambdaQuery.last("ORDER BY " + orderBy); // 另外个思路:https://blog.csdn.net/m0_59084856/article/details/138450913 } else { throw new IllegalArgumentException("Unsupported wrapper type: " + wrapper.getClass().getName()); @@ -81,6 +93,22 @@ public class MyBatisUtils { } + public static boolean isAscOrder(String order) { + return SortingField.ORDER_ASC.equals(order); + } + + public static String getOrderDirection(String order) { + return isAscOrder(order) ? "ASC" : "DESC"; + } + + private static String buildSafeOrderColumn(String field) { + String columnName = StrUtil.toUnderlineCase(field); + if (StrUtil.isEmpty(columnName) || !SAFE_COLUMN_NAME_PATTERN.matcher(columnName).matches()) { + return null; + } + return columnName; + } + /** * 将拦截器添加到链中 * 由于 MybatisPlusInterceptor 不支持添加拦截器,所以只能全量设置 diff --git a/yudao-framework/yudao-spring-boot-starter-mybatis/src/test/java/cn/iocoder/yudao/framework/mybatis/core/util/MyBatisUtilsTest.java b/yudao-framework/yudao-spring-boot-starter-mybatis/src/test/java/cn/iocoder/yudao/framework/mybatis/core/util/MyBatisUtilsTest.java new file mode 100644 index 000000000..43f5611dc --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-mybatis/src/test/java/cn/iocoder/yudao/framework/mybatis/core/util/MyBatisUtilsTest.java @@ -0,0 +1,106 @@ +package cn.iocoder.yudao.framework.mybatis.core.util; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.SortingField; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.OrderItem; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * {@link MyBatisUtils} 的单元测试 + */ +public class MyBatisUtilsTest { + + @Test + public void testBuildPage_sortingFields() { + // 准备参数 + PageParam pageParam = new PageParam(); + pageParam.setPageNo(2); + pageParam.setPageSize(20); + List sortingFields = Arrays.asList( + new SortingField("userName", SortingField.ORDER_ASC), + new SortingField("u.id", SortingField.ORDER_DESC), + new SortingField("name desc", SortingField.ORDER_DESC)); + + // 调用 + Page page = MyBatisUtils.buildPage(pageParam, sortingFields); + + // 断言 + assertEquals(2, page.getCurrent()); + assertEquals(20, page.getSize()); + assertEquals(2, page.orders().size()); + assertOrderItem(page.orders().get(0), "user_name", true); + assertOrderItem(page.orders().get(1), "u.id", false); + } + + @Test + public void testAddOrder_queryWrapper() { + // 准备参数 + QueryWrapper query = new QueryWrapper<>(); + List sortingFields = Arrays.asList( + new SortingField("userName", SortingField.ORDER_ASC), + new SortingField("u.id", SortingField.ORDER_DESC), + new SortingField("name;drop", SortingField.ORDER_ASC)); + + // 调用 + MyBatisUtils.addOrder(query, sortingFields); + + // 断言 + assertEquals(" ORDER BY user_name ASC,u.id DESC", query.getSqlSegment()); + } + + @Test + public void testAddOrder_lambdaQueryWrapper() { + // 准备参数 + LambdaQueryWrapper query = new LambdaQueryWrapper<>(); + List sortingFields = Arrays.asList( + new SortingField("userName", SortingField.ORDER_ASC), + new SortingField("u.id", SortingField.ORDER_DESC), + new SortingField("name`", SortingField.ORDER_ASC)); + + // 调用 + MyBatisUtils.addOrder(query, sortingFields); + + // 断言 + assertEquals(" ORDER BY user_name ASC, u.id DESC", query.getSqlSegment()); + } + + @Test + public void testAddOrder_lambdaQueryWrapper_invalidSortingFields() { + // 准备参数 + LambdaQueryWrapper query = new LambdaQueryWrapper<>(); + List sortingFields = Arrays.asList( + new SortingField("name desc", SortingField.ORDER_ASC), + new SortingField("name;drop", SortingField.ORDER_DESC)); + + // 调用 + MyBatisUtils.addOrder(query, sortingFields); + + // 断言 + assertEquals("", query.getSqlSegment()); + } + + @Test + public void testOrderDirection() { + assertTrue(MyBatisUtils.isAscOrder(SortingField.ORDER_ASC)); + assertFalse(MyBatisUtils.isAscOrder(SortingField.ORDER_DESC)); + assertEquals("ASC", MyBatisUtils.getOrderDirection(SortingField.ORDER_ASC)); + assertEquals("DESC", MyBatisUtils.getOrderDirection(SortingField.ORDER_DESC)); + assertEquals("DESC", MyBatisUtils.getOrderDirection(null)); + } + + private void assertOrderItem(OrderItem orderItem, String column, boolean asc) { + assertEquals(column, orderItem.getColumn()); + assertEquals(asc, orderItem.isAsc()); + } + +} diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/filter/ApiAccessLogFilter.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/filter/ApiAccessLogFilter.java index e03673995..efaed77f1 100644 --- a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/filter/ApiAccessLogFilter.java +++ b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/filter/ApiAccessLogFilter.java @@ -69,7 +69,7 @@ public class ApiAccessLogFilter extends ApiRequestFilter { LocalDateTime beginTime = LocalDateTime.now(); // 提前获得参数,避免 XssFilter 过滤处理 Map queryString = ServletUtils.getParamMap(request); - String requestBody = ServletUtils.isJsonRequest(request) ? ServletUtils.getBody(request) : null; + String requestBody = ServletUtils.getBody(request); try { // 继续过滤器 diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/interceptor/ApiAccessLogInterceptor.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/interceptor/ApiAccessLogInterceptor.java index 7d6b45646..67b7f5a53 100644 --- a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/interceptor/ApiAccessLogInterceptor.java +++ b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/interceptor/ApiAccessLogInterceptor.java @@ -44,7 +44,7 @@ public class ApiAccessLogInterceptor implements HandlerInterceptor { // 打印 request 日志 if (!SpringUtils.isProd()) { Map queryString = ServletUtils.getParamMap(request); - String requestBody = ServletUtils.isJsonRequest(request) ? ServletUtils.getBody(request) : null; + String requestBody = ServletUtils.getBody(request); if (CollUtil.isEmpty(queryString) && StrUtil.isEmpty(requestBody)) { log.info("[preHandle][开始请求 URL({}) 无参数]", request.getRequestURI()); } else { diff --git a/yudao-module-mall/yudao-module-trade-server/src/main/resources/mapper/brokerage/BrokerageUserMapper.xml b/yudao-module-mall/yudao-module-trade-server/src/main/resources/mapper/brokerage/BrokerageUserMapper.xml index 066f75d4e..69ee4ede5 100644 --- a/yudao-module-mall/yudao-module-trade-server/src/main/resources/mapper/brokerage/BrokerageUserMapper.xml +++ b/yudao-module-mall/yudao-module-trade-server/src/main/resources/mapper/brokerage/BrokerageUserMapper.xml @@ -2,9 +2,19 @@ + + + + ASC + + + DESC + + + +