Commit a426cc2f4b8ca83cdf87967c13d68a169822d729

Authored by YunaiV
1 parent e72c0249

fix: 修复 findInSet SQL 注入风险

- 调整 MyBatisUtils.findInSet 使用 MyBatis-Plus 参数绑定
- 增加 columnName 白名单校验,避免列名被注入
- 补充 H2 兼容实现,恢复相关单测覆盖
- 替换各模块动态 FIND_IN_SET 字符串拼接写法
- 补充单参数、多参数绑定场景单测
Showing 17 changed files with 154 additions and 48 deletions
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/enums/DbTypeEnum.java
@@ -20,51 +20,49 @@ public enum DbTypeEnum { @@ -20,51 +20,49 @@ public enum DbTypeEnum {
20 20
21 /** 21 /**
22 * H2 22 * H2
23 - *  
24 - * 注意:H2 不支持 find_in_set 函数  
25 */ 23 */
26 - H2(DbType.H2, "H2", ""), 24 + H2(DbType.H2, "H2", "POSITION(',' || CAST(#{value} AS VARCHAR) || ',' IN ',' || #{column} || ',') > 0"),
27 25
28 /** 26 /**
29 * MySQL 27 * MySQL
30 */ 28 */
31 - MY_SQL(DbType.MYSQL, "MySQL", "FIND_IN_SET('#{value}', #{column}) <> 0"), 29 + MY_SQL(DbType.MYSQL, "MySQL", "FIND_IN_SET(#{value}, #{column}) <> 0"),
32 30
33 /** 31 /**
34 * Oracle 32 * Oracle
35 */ 33 */
36 - ORACLE(DbType.ORACLE, "Oracle", "FIND_IN_SET('#{value}', #{column}) <> 0"), 34 + ORACLE(DbType.ORACLE, "Oracle", "INSTR(',' || #{column} || ',', ',' || #{value} || ',') > 0"),
37 35
38 /** 36 /**
39 * PostgreSQL 37 * PostgreSQL
40 * 38 *
41 * 华为 openGauss 使用 ProductName 与 PostgreSQL 相同 39 * 华为 openGauss 使用 ProductName 与 PostgreSQL 相同
42 */ 40 */
43 - POSTGRE_SQL(DbType.POSTGRE_SQL,"PostgreSQL", "POSITION('#{value}' IN #{column}) <> 0"), 41 + POSTGRE_SQL(DbType.POSTGRE_SQL, "PostgreSQL", "POSITION(',' || CAST(#{value} AS VARCHAR) || ',' IN ',' || #{column} || ',') > 0"),
44 42
45 /** 43 /**
46 * SQL Server 44 * SQL Server
47 */ 45 */
48 - SQL_SERVER(DbType.SQL_SERVER, "Microsoft SQL Server", "CHARINDEX(',' + #{value} + ',', ',' + #{column} + ',') <> 0"), 46 + SQL_SERVER(DbType.SQL_SERVER, "Microsoft SQL Server", "CHARINDEX(',' + CAST(#{value} AS varchar(255)) + ',', ',' + #{column} + ',') > 0"),
49 /** 47 /**
50 * SQL Server 2005 48 * SQL Server 2005
51 */ 49 */
52 - SQL_SERVER2005(DbType.SQL_SERVER2005, "Microsoft SQL Server 2005", "CHARINDEX(',' + #{value} + ',', ',' + #{column} + ',') <> 0"), 50 + SQL_SERVER2005(DbType.SQL_SERVER2005, "Microsoft SQL Server 2005", "CHARINDEX(',' + CAST(#{value} AS varchar(255)) + ',', ',' + #{column} + ',') > 0"),
53 51
54 /** 52 /**
55 * 达梦 53 * 达梦
56 */ 54 */
57 - DM(DbType.DM, "DM DBMS", "FIND_IN_SET('#{value}', #{column}) <> 0"), 55 + DM(DbType.DM, "DM DBMS", "FIND_IN_SET(#{value}, #{column}) <> 0"),
58 56
59 /** 57 /**
60 * 人大金仓 58 * 人大金仓
61 */ 59 */
62 - KINGBASE_ES(DbType.KINGBASE_ES, "KingbaseES", "POSITION('#{value}' IN #{column}) <> 0"), 60 + KINGBASE_ES(DbType.KINGBASE_ES, "KingbaseES", "POSITION(',' || CAST(#{value} AS VARCHAR) || ',' IN ',' || #{column} || ',') > 0"),
63 61
64 /** 62 /**
65 * OceanBase 63 * OceanBase
66 */ 64 */
67 - OCEAN_BASE(DbType.OCEAN_BASE, "OceanBase", "FIND_IN_SET('#{value}', #{column}) <> 0") 65 + OCEAN_BASE(DbType.OCEAN_BASE, "OceanBase", "FIND_IN_SET(#{value}, #{column}) <> 0")
68 66
69 ; 67 ;
70 68
@@ -95,7 +93,9 @@ public enum DbTypeEnum { @@ -95,7 +93,9 @@ public enum DbTypeEnum {
95 } 93 }
96 94
97 public static String getFindInSetTemplate(DbType dbType) { 95 public static String getFindInSetTemplate(DbType dbType) {
98 - return Optional.of(MAP_BY_MP.get(dbType).getFindInSetTemplate()) 96 + return Optional.ofNullable(MAP_BY_MP.get(dbType))
  97 + .map(DbTypeEnum::getFindInSetTemplate)
  98 + .filter(StrUtil::isNotBlank)
99 .orElseThrow(() -> new IllegalArgumentException("FIND_IN_SET not supported")); 99 .orElseThrow(() -> new IllegalArgumentException("FIND_IN_SET not supported"));
100 } 100 }
101 } 101 }
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/util/MyBatisUtils.java
@@ -34,6 +34,10 @@ public class MyBatisUtils { @@ -34,6 +34,10 @@ public class MyBatisUtils {
34 34
35 private static final Pattern SAFE_COLUMN_NAME_PATTERN = Pattern.compile("^[a-zA-Z0-9_]+(\\.[a-zA-Z0-9_]+)*$"); 35 private static final Pattern SAFE_COLUMN_NAME_PATTERN = Pattern.compile("^[a-zA-Z0-9_]+(\\.[a-zA-Z0-9_]+)*$");
36 36
  37 + private static final String FIND_IN_SET_VALUE_PLACEHOLDER = "#{value}";
  38 +
  39 + private static final String FIND_IN_SET_COLUMN_PLACEHOLDER = "#{column}";
  40 +
37 public static <T> Page<T> buildPage(PageParam pageParam) { 41 public static <T> Page<T> buildPage(PageParam pageParam) {
38 return buildPage(pageParam, null); 42 return buildPage(pageParam, null);
39 } 43 }
@@ -157,15 +161,43 @@ public class MyBatisUtils { @@ -157,15 +161,43 @@ public class MyBatisUtils {
157 /** 161 /**
158 * 跨数据库的 find_in_set 实现 162 * 跨数据库的 find_in_set 实现
159 * 163 *
160 - * @param column 字段名称  
161 - * @param value 查询值(不带单引号) 164 + * @param columnName 字段名称
  165 + * @return sql
  166 + */
  167 + public static String findInSet(String columnName) {
  168 + return findInSet(columnName, 0);
  169 + }
  170 +
  171 + /**
  172 + * 跨数据库的 find_in_set 实现,适用于同一个 apply 语句中有多个参数的场景
  173 + *
  174 + * @param columnName 字段名称
  175 + * @param paramIndex apply 参数序号
162 * @return sql 176 * @return sql
163 */ 177 */
164 - public static String findInSet(String column, Object value) { 178 + public static String findInSetWithParamIndex(String columnName, int paramIndex) {
  179 + return findInSet(columnName, paramIndex);
  180 + }
  181 +
  182 + private static String findInSet(String columnName, int paramIndex) {
165 DbType dbType = JdbcUtils.getDbType(); 183 DbType dbType = JdbcUtils.getDbType();
  184 + return findInSet(dbType, columnName, paramIndex);
  185 + }
  186 +
  187 + static String findInSet(DbType dbType, String columnName, int paramIndex) {
  188 + if (!isSafeColumnName(columnName)) {
  189 + throw new IllegalArgumentException("Invalid column name: " + columnName);
  190 + }
  191 + if (paramIndex < 0) {
  192 + throw new IllegalArgumentException("Invalid param index: " + paramIndex);
  193 + }
166 return DbTypeEnum.getFindInSetTemplate(dbType) 194 return DbTypeEnum.getFindInSetTemplate(dbType)
167 - .replace("#{column}", column)  
168 - .replace("#{value}", StrUtil.toString(value)); 195 + .replace(FIND_IN_SET_COLUMN_PLACEHOLDER, columnName)
  196 + .replace(FIND_IN_SET_VALUE_PLACEHOLDER, "{" + paramIndex + "}");
  197 + }
  198 +
  199 + private static boolean isSafeColumnName(String columnName) {
  200 + return StrUtil.isNotEmpty(columnName) && SAFE_COLUMN_NAME_PATTERN.matcher(columnName).matches();
169 } 201 }
170 202
171 /** 203 /**
yudao-framework/yudao-spring-boot-starter-mybatis/src/test/java/cn/iocoder/yudao/framework/mybatis/core/util/MyBatisUtilsTest.java
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.framework.mybatis.core.util; @@ -2,6 +2,7 @@ package cn.iocoder.yudao.framework.mybatis.core.util;
2 2
3 import cn.iocoder.yudao.framework.common.pojo.PageParam; 3 import cn.iocoder.yudao.framework.common.pojo.PageParam;
4 import cn.iocoder.yudao.framework.common.pojo.SortingField; 4 import cn.iocoder.yudao.framework.common.pojo.SortingField;
  5 +import com.baomidou.mybatisplus.annotation.DbType;
5 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; 6 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
6 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; 7 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
7 import com.baomidou.mybatisplus.core.metadata.OrderItem; 8 import com.baomidou.mybatisplus.core.metadata.OrderItem;
@@ -13,6 +14,7 @@ import java.util.List; @@ -13,6 +14,7 @@ import java.util.List;
13 14
14 import static org.junit.jupiter.api.Assertions.assertEquals; 15 import static org.junit.jupiter.api.Assertions.assertEquals;
15 import static org.junit.jupiter.api.Assertions.assertFalse; 16 import static org.junit.jupiter.api.Assertions.assertFalse;
  17 +import static org.junit.jupiter.api.Assertions.assertThrows;
16 import static org.junit.jupiter.api.Assertions.assertTrue; 18 import static org.junit.jupiter.api.Assertions.assertTrue;
17 19
18 /** 20 /**
@@ -98,6 +100,71 @@ public class MyBatisUtilsTest { @@ -98,6 +100,71 @@ public class MyBatisUtilsTest {
98 assertEquals("DESC", MyBatisUtils.getOrderDirection(null)); 100 assertEquals("DESC", MyBatisUtils.getOrderDirection(null));
99 } 101 }
100 102
  103 + @Test
  104 + public void testFindInSet() {
  105 + assertEquals("FIND_IN_SET({0}, websites) <> 0",
  106 + MyBatisUtils.findInSet(DbType.MYSQL, "websites", 0));
  107 + assertEquals("POSITION(',' || CAST({0} AS VARCHAR) || ',' IN ',' || websites || ',') > 0",
  108 + MyBatisUtils.findInSet(DbType.H2, "websites", 0));
  109 + assertEquals("INSTR(',' || t.websites || ',', ',' || {0} || ',') > 0",
  110 + MyBatisUtils.findInSet(DbType.ORACLE, "t.websites", 0));
  111 + assertEquals("POSITION(',' || CAST({1} AS VARCHAR) || ',' IN ',' || websites || ',') > 0",
  112 + MyBatisUtils.findInSet(DbType.POSTGRE_SQL, "websites", 1));
  113 + assertEquals("CHARINDEX(',' + CAST({2} AS varchar(255)) + ',', ',' + websites + ',') > 0",
  114 + MyBatisUtils.findInSet(DbType.SQL_SERVER, "websites", 2));
  115 + }
  116 +
  117 + @Test
  118 + public void testFindInSet_invalidColumnName() {
  119 + assertThrows(IllegalArgumentException.class,
  120 + () -> MyBatisUtils.findInSet(DbType.MYSQL, "websites;drop table system_tenant", 0));
  121 + assertThrows(IllegalArgumentException.class,
  122 + () -> MyBatisUtils.findInSet(DbType.MYSQL, "FIND_IN_SET(value, websites)", 0));
  123 + }
  124 +
  125 + @Test
  126 + public void testFindInSet_invalidParamIndex() {
  127 + assertThrows(IllegalArgumentException.class,
  128 + () -> MyBatisUtils.findInSet(DbType.MYSQL, "websites", -1));
  129 + }
  130 +
  131 + @Test
  132 + public void testFindInSet_applyBindsValue() {
  133 + // 准备参数
  134 + QueryWrapper<Object> query = new QueryWrapper<>();
  135 + String value = "test' OR 1 = 1";
  136 +
  137 + // 调用
  138 + query.apply(MyBatisUtils.findInSet(DbType.MYSQL, "to_mails", 0), value);
  139 +
  140 + // 断言:SQL 片段里只有 MyBatis Plus 参数占位,用户输入不会被直接拼接进去
  141 + assertEquals("(FIND_IN_SET(#{ew.paramNameValuePairs.MPGENVAL1}, to_mails) <> 0)",
  142 + query.getSqlSegment());
  143 + assertFalse(query.getSqlSegment().contains(value));
  144 + assertEquals(value, query.getParamNameValuePairs().get("MPGENVAL1"));
  145 + }
  146 +
  147 + @Test
  148 + public void testFindInSet_applyBindsMultipleValues() {
  149 + // 准备参数
  150 + QueryWrapper<Object> query = new QueryWrapper<>();
  151 + String value1 = "1' OR 1 = 1";
  152 + String value2 = "2' OR 1 = 1";
  153 +
  154 + // 调用
  155 + query.apply(MyBatisUtils.findInSet(DbType.MYSQL, "tag_ids", 0)
  156 + + " OR " + MyBatisUtils.findInSet(DbType.MYSQL, "tag_ids", 1), value1, value2);
  157 +
  158 + // 断言:多个参数都由 MyBatis Plus 生成占位符,不拼接用户输入
  159 + assertEquals("(FIND_IN_SET(#{ew.paramNameValuePairs.MPGENVAL1}, tag_ids) <> 0"
  160 + + " OR FIND_IN_SET(#{ew.paramNameValuePairs.MPGENVAL2}, tag_ids) <> 0)",
  161 + query.getSqlSegment());
  162 + assertFalse(query.getSqlSegment().contains(value1));
  163 + assertFalse(query.getSqlSegment().contains(value2));
  164 + assertEquals(value1, query.getParamNameValuePairs().get("MPGENVAL1"));
  165 + assertEquals(value2, query.getParamNameValuePairs().get("MPGENVAL2"));
  166 + }
  167 +
101 private void assertOrderItem(OrderItem orderItem, String column, boolean asc) { 168 private void assertOrderItem(OrderItem orderItem, String column, boolean asc) {
102 assertEquals(column, orderItem.getColumn()); 169 assertEquals(column, orderItem.getColumn());
103 assertEquals(asc, orderItem.isAsc()); 170 assertEquals(asc, orderItem.isAsc());
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/customer/CrmCustomerLimitConfigMapper.java
@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.crm.dal.mysql.customer; @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.crm.dal.mysql.customer;
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; 4 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
5 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; 5 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  6 +import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
6 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.limitconfig.CrmCustomerLimitConfigPageReqVO; 7 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.limitconfig.CrmCustomerLimitConfigPageReqVO;
7 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerLimitConfigDO; 8 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerLimitConfigDO;
8 import org.apache.ibatis.annotations.Mapper; 9 import org.apache.ibatis.annotations.Mapper;
@@ -28,9 +29,9 @@ public interface CrmCustomerLimitConfigMapper extends BaseMapperX&lt;CrmCustomerLim @@ -28,9 +29,9 @@ public interface CrmCustomerLimitConfigMapper extends BaseMapperX&lt;CrmCustomerLim
28 LambdaQueryWrapperX<CrmCustomerLimitConfigDO> query = new LambdaQueryWrapperX<CrmCustomerLimitConfigDO>() 29 LambdaQueryWrapperX<CrmCustomerLimitConfigDO> query = new LambdaQueryWrapperX<CrmCustomerLimitConfigDO>()
29 .eq(CrmCustomerLimitConfigDO::getType, type); 30 .eq(CrmCustomerLimitConfigDO::getType, type);
30 query.and(w -> { 31 query.and(w -> {
31 - w.apply("FIND_IN_SET({0}, user_ids) > 0", userId); 32 + w.apply(MyBatisUtils.findInSet("user_ids"), userId);
32 if (deptId != null) { 33 if (deptId != null) {
33 - w.or().apply("FIND_IN_SET({0}, dept_ids) > 0", deptId); 34 + w.or().apply(MyBatisUtils.findInSet("dept_ids"), deptId);
34 } 35 }
35 }); 36 });
36 return selectList(query); 37 return selectList(query);
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/message/ImChannelMessageMapper.java
@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.im.dal.mysql.message; @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.im.dal.mysql.message;
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; 4 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
5 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; 5 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  6 +import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
6 import cn.iocoder.yudao.module.im.controller.admin.manager.message.vo.channel.ImChannelMessagePageReqVO; 7 import cn.iocoder.yudao.module.im.controller.admin.manager.message.vo.channel.ImChannelMessagePageReqVO;
7 import cn.iocoder.yudao.module.im.dal.dataobject.message.ImChannelMessageDO; 8 import cn.iocoder.yudao.module.im.dal.dataobject.message.ImChannelMessageDO;
8 import org.apache.ibatis.annotations.Mapper; 9 import org.apache.ibatis.annotations.Mapper;
@@ -32,7 +33,7 @@ public interface ImChannelMessageMapper extends BaseMapperX&lt;ImChannelMessageDO&gt; @@ -32,7 +33,7 @@ public interface ImChannelMessageMapper extends BaseMapperX&lt;ImChannelMessageDO&gt;
32 .gt(ImChannelMessageDO::getId, minId) 33 .gt(ImChannelMessageDO::getId, minId)
33 .and(w -> w.isNull(ImChannelMessageDO::getReceiverUserIds) 34 .and(w -> w.isNull(ImChannelMessageDO::getReceiverUserIds)
34 .or().eq(ImChannelMessageDO::getReceiverUserIds, "") 35 .or().eq(ImChannelMessageDO::getReceiverUserIds, "")
35 - .or().apply("FIND_IN_SET({0}, receiver_user_ids)", userId)) 36 + .or().apply(MyBatisUtils.findInSet("receiver_user_ids"), userId))
36 .orderByAsc(ImChannelMessageDO::getId) 37 .orderByAsc(ImChannelMessageDO::getId)
37 .last("LIMIT " + size)); 38 .last("LIMIT " + size));
38 } 39 }
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/alert/IotAlertConfigMapper.java
@@ -33,7 +33,7 @@ public interface IotAlertConfigMapper extends BaseMapperX&lt;IotAlertConfigDO&gt; { @@ -33,7 +33,7 @@ public interface IotAlertConfigMapper extends BaseMapperX&lt;IotAlertConfigDO&gt; {
33 default List<IotAlertConfigDO> selectListBySceneRuleIdAndStatus(Long sceneRuleId, Integer status) { 33 default List<IotAlertConfigDO> selectListBySceneRuleIdAndStatus(Long sceneRuleId, Integer status) {
34 return selectList(new LambdaQueryWrapperX<IotAlertConfigDO>() 34 return selectList(new LambdaQueryWrapperX<IotAlertConfigDO>()
35 .eq(IotAlertConfigDO::getStatus, status) 35 .eq(IotAlertConfigDO::getStatus, status)
36 - .apply(MyBatisUtils.findInSet("scene_rule_ids", sceneRuleId))); 36 + .apply(MyBatisUtils.findInSet("scene_rule_ids"), sceneRuleId));
37 } 37 }
38 38
39 -}  
40 \ No newline at end of file 39 \ No newline at end of file
  40 +}
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java
@@ -4,6 +4,7 @@ import cn.hutool.core.util.ObjectUtil; @@ -4,6 +4,7 @@ import cn.hutool.core.util.ObjectUtil;
4 import cn.iocoder.yudao.framework.common.pojo.PageResult; 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; 5 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
6 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; 6 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  7 +import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
7 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDevicePageReqVO; 8 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDevicePageReqVO;
8 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; 9 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
9 import cn.iocoder.yudao.module.iot.enums.product.IotProductDeviceTypeEnum; 10 import cn.iocoder.yudao.module.iot.enums.product.IotProductDeviceTypeEnum;
@@ -34,7 +35,7 @@ public interface IotDeviceMapper extends BaseMapperX&lt;IotDeviceDO&gt; { @@ -34,7 +35,7 @@ public interface IotDeviceMapper extends BaseMapperX&lt;IotDeviceDO&gt; {
34 .likeIfPresent(IotDeviceDO::getNickname, reqVO.getNickname()) 35 .likeIfPresent(IotDeviceDO::getNickname, reqVO.getNickname())
35 .eqIfPresent(IotDeviceDO::getState, reqVO.getStatus()) 36 .eqIfPresent(IotDeviceDO::getState, reqVO.getStatus())
36 .eqIfPresent(IotDeviceDO::getGatewayId, reqVO.getGatewayId()) 37 .eqIfPresent(IotDeviceDO::getGatewayId, reqVO.getGatewayId())
37 - .apply(ObjectUtil.isNotNull(reqVO.getGroupId()), "FIND_IN_SET(" + reqVO.getGroupId() + ",group_ids) > 0") 38 + .apply(ObjectUtil.isNotNull(reqVO.getGroupId()), MyBatisUtils.findInSet("group_ids"), reqVO.getGroupId())
38 .orderByDesc(IotDeviceDO::getId)); 39 .orderByDesc(IotDeviceDO::getId));
39 } 40 }
40 41
@@ -72,7 +73,7 @@ public interface IotDeviceMapper extends BaseMapperX&lt;IotDeviceDO&gt; { @@ -72,7 +73,7 @@ public interface IotDeviceMapper extends BaseMapperX&lt;IotDeviceDO&gt; {
72 73
73 default Long selectCountByGroupId(Long groupId) { 74 default Long selectCountByGroupId(Long groupId) {
74 return selectCount(new LambdaQueryWrapperX<IotDeviceDO>() 75 return selectCount(new LambdaQueryWrapperX<IotDeviceDO>()
75 - .apply("FIND_IN_SET(" + groupId + ",group_ids) > 0")); 76 + .apply(MyBatisUtils.findInSet("group_ids"), groupId));
76 } 77 }
77 78
78 default Long selectCountByCreateTime(@Nullable LocalDateTime createTime) { 79 default Long selectCountByCreateTime(@Nullable LocalDateTime createTime) {
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/rule/IotDataRuleMapper.java
@@ -28,7 +28,7 @@ public interface IotDataRuleMapper extends BaseMapperX&lt;IotDataRuleDO&gt; { @@ -28,7 +28,7 @@ public interface IotDataRuleMapper extends BaseMapperX&lt;IotDataRuleDO&gt; {
28 28
29 default List<IotDataRuleDO> selectListBySinkId(Long sinkId) { 29 default List<IotDataRuleDO> selectListBySinkId(Long sinkId) {
30 return selectList(new LambdaQueryWrapperX<IotDataRuleDO>() 30 return selectList(new LambdaQueryWrapperX<IotDataRuleDO>()
31 - .apply(MyBatisUtils.findInSet("sink_ids", sinkId))); 31 + .apply(MyBatisUtils.findInSet("sink_ids"), sinkId));
32 } 32 }
33 33
34 default List<IotDataRuleDO> selectListByStatus(Integer status) { 34 default List<IotDataRuleDO> selectListByStatus(Integer status) {
@@ -39,4 +39,4 @@ public interface IotDataRuleMapper extends BaseMapperX&lt;IotDataRuleDO&gt; { @@ -39,4 +39,4 @@ public interface IotDataRuleMapper extends BaseMapperX&lt;IotDataRuleDO&gt; {
39 return selectOne(IotDataRuleDO::getName, name); 39 return selectOne(IotDataRuleDO::getName, name);
40 } 40 }
41 41
42 -}  
43 \ No newline at end of file 42 \ No newline at end of file
  43 +}
yudao-module-mall/yudao-module-promotion/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/coupon/CouponTemplateMapper.java
@@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; @@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
5 import cn.iocoder.yudao.framework.common.pojo.PageResult; 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; 6 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
7 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; 7 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  8 +import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
8 import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplatePageReqVO; 9 import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplatePageReqVO;
9 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO; 10 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO;
10 import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTemplateValidityTypeEnum; 11 import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTemplateValidityTypeEnum;
@@ -34,7 +35,7 @@ public interface CouponTemplateMapper extends BaseMapperX&lt;CouponTemplateDO&gt; { @@ -34,7 +35,7 @@ public interface CouponTemplateMapper extends BaseMapperX&lt;CouponTemplateDO&gt; {
34 .eqIfPresent(CouponTemplateDO::getDiscountType, reqVO.getDiscountType()) 35 .eqIfPresent(CouponTemplateDO::getDiscountType, reqVO.getDiscountType())
35 .betweenIfPresent(CouponTemplateDO::getCreateTime, reqVO.getCreateTime()) 36 .betweenIfPresent(CouponTemplateDO::getCreateTime, reqVO.getCreateTime())
36 .eqIfPresent(CouponTemplateDO::getProductScope, reqVO.getProductScope()) 37 .eqIfPresent(CouponTemplateDO::getProductScope, reqVO.getProductScope())
37 - .and(reqVO.getProductScopeValue() != null, w -> w.apply("FIND_IN_SET({0}, product_scope_values)", 38 + .and(reqVO.getProductScopeValue() != null, w -> w.apply(MyBatisUtils.findInSet("product_scope_values"),
38 reqVO.getProductScopeValue())) 39 reqVO.getProductScopeValue()))
39 .and(canTakeConsumer != null, canTakeConsumer) 40 .and(canTakeConsumer != null, canTakeConsumer)
40 .orderByDesc(CouponTemplateDO::getId)); 41 .orderByDesc(CouponTemplateDO::getId));
@@ -61,7 +62,7 @@ public interface CouponTemplateMapper extends BaseMapperX&lt;CouponTemplateDO&gt; { @@ -61,7 +62,7 @@ public interface CouponTemplateMapper extends BaseMapperX&lt;CouponTemplateDO&gt; {
61 Consumer<LambdaQueryWrapper<CouponTemplateDO>> canTakeConsumer = buildCanTakeQueryConsumer(canTakeTypes); 62 Consumer<LambdaQueryWrapper<CouponTemplateDO>> canTakeConsumer = buildCanTakeQueryConsumer(canTakeTypes);
62 return selectList(new LambdaQueryWrapperX<CouponTemplateDO>() 63 return selectList(new LambdaQueryWrapperX<CouponTemplateDO>()
63 .eqIfPresent(CouponTemplateDO::getProductScope, productScope) 64 .eqIfPresent(CouponTemplateDO::getProductScope, productScope)
64 - .and(productScopeValue != null, w -> w.apply("FIND_IN_SET({0}, product_scope_values)", 65 + .and(productScopeValue != null, w -> w.apply(MyBatisUtils.findInSet("product_scope_values"),
65 productScopeValue)) 66 productScopeValue))
66 .and(canTakeConsumer != null, canTakeConsumer) 67 .and(canTakeConsumer != null, canTakeConsumer)
67 .last(" LIMIT " + count) 68 .last(" LIMIT " + count)
yudao-module-mall/yudao-module-promotion/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/reward/RewardActivityMapper.java
1 package cn.iocoder.yudao.module.promotion.dal.mysql.reward; 1 package cn.iocoder.yudao.module.promotion.dal.mysql.reward;
2 2
3 -import cn.hutool.core.util.StrUtil;  
4 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; 4 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
6 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; 5 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  6 +import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
7 import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityPageReqVO; 7 import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityPageReqVO;
8 import cn.iocoder.yudao.module.promotion.dal.dataobject.reward.RewardActivityDO; 8 import cn.iocoder.yudao.module.promotion.dal.dataobject.reward.RewardActivityDO;
9 import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum; 9 import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;
@@ -14,6 +14,7 @@ import java.util.Collection; @@ -14,6 +14,7 @@ import java.util.Collection;
14 import java.util.List; 14 import java.util.List;
15 import java.util.function.Function; 15 import java.util.function.Function;
16 import java.util.stream.Collectors; 16 import java.util.stream.Collectors;
  17 +import java.util.stream.IntStream;
17 18
18 /** 19 /**
19 * 满减送活动 Mapper 20 * 满减送活动 Mapper
@@ -34,18 +35,18 @@ public interface RewardActivityMapper extends BaseMapperX&lt;RewardActivityDO&gt; { @@ -34,18 +35,18 @@ public interface RewardActivityMapper extends BaseMapperX&lt;RewardActivityDO&gt; {
34 Collection<Long> categoryIds, 35 Collection<Long> categoryIds,
35 Integer status) { 36 Integer status) {
36 LocalDateTime now = LocalDateTime.now(); 37 LocalDateTime now = LocalDateTime.now();
37 - Function<Collection<Long>, String> productScopeValuesFindInSetFunc = ids -> ids.stream()  
38 - .map(id -> StrUtil.format("FIND_IN_SET({}, product_scope_values) ", id)) 38 + Function<Collection<Long>, String> productScopeValuesFindInSetFunc = ids -> IntStream.range(0, ids.size())
  39 + .mapToObj(index -> MyBatisUtils.findInSetWithParamIndex("product_scope_values", index))
39 .collect(Collectors.joining(" OR ")); 40 .collect(Collectors.joining(" OR "));
40 return selectList(new LambdaQueryWrapperX<RewardActivityDO>() 41 return selectList(new LambdaQueryWrapperX<RewardActivityDO>()
41 .eq(RewardActivityDO::getStatus, status) 42 .eq(RewardActivityDO::getStatus, status)
42 .lt(RewardActivityDO::getStartTime, now) 43 .lt(RewardActivityDO::getStartTime, now)
43 .gt(RewardActivityDO::getEndTime, now) 44 .gt(RewardActivityDO::getEndTime, now)
44 .and(i -> i.eq(RewardActivityDO::getProductScope, PromotionProductScopeEnum.SPU.getScope()) 45 .and(i -> i.eq(RewardActivityDO::getProductScope, PromotionProductScopeEnum.SPU.getScope())
45 - .and(i1 -> i1.apply(productScopeValuesFindInSetFunc.apply(spuIds))) 46 + .and(i1 -> i1.apply(productScopeValuesFindInSetFunc.apply(spuIds), spuIds.toArray()))
46 .or(i1 -> i1.eq(RewardActivityDO::getProductScope, PromotionProductScopeEnum.ALL.getScope())) 47 .or(i1 -> i1.eq(RewardActivityDO::getProductScope, PromotionProductScopeEnum.ALL.getScope()))
47 .or(i1 -> i1.eq(RewardActivityDO::getProductScope, PromotionProductScopeEnum.CATEGORY.getScope()) 48 .or(i1 -> i1.eq(RewardActivityDO::getProductScope, PromotionProductScopeEnum.CATEGORY.getScope())
48 - .and(i2 -> i2.apply(productScopeValuesFindInSetFunc.apply(categoryIds))))) 49 + .and(i2 -> i2.apply(productScopeValuesFindInSetFunc.apply(categoryIds), categoryIds.toArray()))))
49 .orderByDesc(RewardActivityDO::getId) 50 .orderByDesc(RewardActivityDO::getId)
50 ); 51 );
51 } 52 }
yudao-module-mall/yudao-module-promotion/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/seckill/seckillactivity/SeckillActivityMapper.java
@@ -5,6 +5,7 @@ import cn.hutool.core.util.ObjectUtil; @@ -5,6 +5,7 @@ import cn.hutool.core.util.ObjectUtil;
5 import cn.iocoder.yudao.framework.common.pojo.PageResult; 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; 6 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
7 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; 7 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  8 +import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
8 import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.SeckillActivityPageReqVO; 9 import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.SeckillActivityPageReqVO;
9 import cn.iocoder.yudao.module.promotion.controller.app.seckill.vo.activity.AppSeckillActivityPageReqVO; 10 import cn.iocoder.yudao.module.promotion.controller.app.seckill.vo.activity.AppSeckillActivityPageReqVO;
10 import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillActivityDO; 11 import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillActivityDO;
@@ -27,7 +28,7 @@ public interface SeckillActivityMapper extends BaseMapperX&lt;SeckillActivityDO&gt; { @@ -27,7 +28,7 @@ public interface SeckillActivityMapper extends BaseMapperX&lt;SeckillActivityDO&gt; {
27 .likeIfPresent(SeckillActivityDO::getName, reqVO.getName()) 28 .likeIfPresent(SeckillActivityDO::getName, reqVO.getName())
28 .eqIfPresent(SeckillActivityDO::getStatus, reqVO.getStatus()) 29 .eqIfPresent(SeckillActivityDO::getStatus, reqVO.getStatus())
29 .betweenIfPresent(SeckillActivityDO::getCreateTime, reqVO.getCreateTime()) 30 .betweenIfPresent(SeckillActivityDO::getCreateTime, reqVO.getCreateTime())
30 - .apply(ObjectUtil.isNotNull(reqVO.getConfigId()), "FIND_IN_SET(" + reqVO.getConfigId() + ", config_ids) > 0") 31 + .apply(ObjectUtil.isNotNull(reqVO.getConfigId()), MyBatisUtils.findInSet("config_ids"), reqVO.getConfigId())
31 .orderByDesc(SeckillActivityDO::getId)); 32 .orderByDesc(SeckillActivityDO::getId));
32 } 33 }
33 34
@@ -70,7 +71,7 @@ public interface SeckillActivityMapper extends BaseMapperX&lt;SeckillActivityDO&gt; { @@ -70,7 +71,7 @@ public interface SeckillActivityMapper extends BaseMapperX&lt;SeckillActivityDO&gt; {
70 .eqIfPresent(SeckillActivityDO::getStatus, status) 71 .eqIfPresent(SeckillActivityDO::getStatus, status)
71 .lt(SeckillActivityDO::getStartTime, dateTime) 72 .lt(SeckillActivityDO::getStartTime, dateTime)
72 .gt(SeckillActivityDO::getEndTime, dateTime)// 开始时间 < 指定时间 < 结束时间,也就是说获取指定时间段的活动 73 .gt(SeckillActivityDO::getEndTime, dateTime)// 开始时间 < 指定时间 < 结束时间,也就是说获取指定时间段的活动
73 - .apply(ObjectUtil.isNotNull(pageReqVO.getConfigId()), "FIND_IN_SET(" + pageReqVO.getConfigId() + ",config_ids) > 0")); 74 + .apply(ObjectUtil.isNotNull(pageReqVO.getConfigId()), MyBatisUtils.findInSet("config_ids"), pageReqVO.getConfigId()));
74 } 75 }
75 76
76 default SeckillActivityDO selectBySpuIdAndStatusAndNow(Long spuId, Integer status) { 77 default SeckillActivityDO selectBySpuIdAndStatusAndNow(Long spuId, Integer status) {
yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/user/MemberUserMapper.java
@@ -2,10 +2,10 @@ package cn.iocoder.yudao.module.member.dal.mysql.user; @@ -2,10 +2,10 @@ package cn.iocoder.yudao.module.member.dal.mysql.user;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.lang.Assert; 4 import cn.hutool.core.lang.Assert;
5 -import cn.hutool.core.util.StrUtil;  
6 import cn.iocoder.yudao.framework.common.pojo.PageResult; 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
7 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; 6 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
8 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; 7 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  8 +import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
9 import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserPageReqVO; 9 import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserPageReqVO;
10 import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO; 10 import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
11 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; 11 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
@@ -13,6 +13,7 @@ import org.apache.ibatis.annotations.Mapper; @@ -13,6 +13,7 @@ import org.apache.ibatis.annotations.Mapper;
13 13
14 import java.util.List; 14 import java.util.List;
15 import java.util.stream.Collectors; 15 import java.util.stream.Collectors;
  16 +import java.util.stream.IntStream;
16 17
17 /** 18 /**
18 * 会员 User Mapper 19 * 会员 User Mapper
@@ -38,10 +39,12 @@ public interface MemberUserMapper extends BaseMapperX&lt;MemberUserDO&gt; { @@ -38,10 +39,12 @@ public interface MemberUserMapper extends BaseMapperX&lt;MemberUserDO&gt; {
38 default PageResult<MemberUserDO> selectPage(MemberUserPageReqVO reqVO) { 39 default PageResult<MemberUserDO> selectPage(MemberUserPageReqVO reqVO) {
39 // 处理 tagIds 过滤条件 40 // 处理 tagIds 过滤条件
40 String tagIdSql = ""; 41 String tagIdSql = "";
  42 + Object[] tagIdParams = new Object[0];
41 if (CollUtil.isNotEmpty(reqVO.getTagIds())) { 43 if (CollUtil.isNotEmpty(reqVO.getTagIds())) {
42 - tagIdSql = reqVO.getTagIds().stream()  
43 - .map(tagId -> "FIND_IN_SET(" + tagId + ", tag_ids)") 44 + tagIdSql = IntStream.range(0, reqVO.getTagIds().size())
  45 + .mapToObj(index -> MyBatisUtils.findInSetWithParamIndex("tag_ids", index))
44 .collect(Collectors.joining(" OR ")); 46 .collect(Collectors.joining(" OR "));
  47 + tagIdParams = reqVO.getTagIds().toArray();
45 } 48 }
46 // 分页查询 49 // 分页查询
47 return selectPage(reqVO, new LambdaQueryWrapperX<MemberUserDO>() 50 return selectPage(reqVO, new LambdaQueryWrapperX<MemberUserDO>()
@@ -52,7 +55,7 @@ public interface MemberUserMapper extends BaseMapperX&lt;MemberUserDO&gt; { @@ -52,7 +55,7 @@ public interface MemberUserMapper extends BaseMapperX&lt;MemberUserDO&gt; {
52 .betweenIfPresent(MemberUserDO::getCreateTime, reqVO.getCreateTime()) 55 .betweenIfPresent(MemberUserDO::getCreateTime, reqVO.getCreateTime())
53 .eqIfPresent(MemberUserDO::getLevelId, reqVO.getLevelId()) 56 .eqIfPresent(MemberUserDO::getLevelId, reqVO.getLevelId())
54 .eqIfPresent(MemberUserDO::getGroupId, reqVO.getGroupId()) 57 .eqIfPresent(MemberUserDO::getGroupId, reqVO.getGroupId())
55 - .apply(StrUtil.isNotEmpty(tagIdSql), tagIdSql) 58 + .apply(CollUtil.isNotEmpty(reqVO.getTagIds()), tagIdSql, tagIdParams)
56 .orderByDesc(MemberUserDO::getId)); 59 .orderByDesc(MemberUserDO::getId));
57 } 60 }
58 61
@@ -66,7 +69,7 @@ public interface MemberUserMapper extends BaseMapperX&lt;MemberUserDO&gt; { @@ -66,7 +69,7 @@ public interface MemberUserMapper extends BaseMapperX&lt;MemberUserDO&gt; {
66 69
67 default Long selectCountByTagId(Long tagId) { 70 default Long selectCountByTagId(Long tagId) {
68 return selectCount(new LambdaQueryWrapperX<MemberUserDO>() 71 return selectCount(new LambdaQueryWrapperX<MemberUserDO>()
69 - .apply("FIND_IN_SET({0}, tag_ids)", tagId)); 72 + .apply(MyBatisUtils.findInSet("tag_ids"), tagId));
70 } 73 }
71 74
72 /** 75 /**
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/qc/template/MesQcTemplateMapper.java
@@ -24,7 +24,7 @@ public interface MesQcTemplateMapper extends BaseMapperX&lt;MesQcTemplateDO&gt; { @@ -24,7 +24,7 @@ public interface MesQcTemplateMapper extends BaseMapperX&lt;MesQcTemplateDO&gt; {
24 .likeIfPresent(MesQcTemplateDO::getName, reqVO.getName()) 24 .likeIfPresent(MesQcTemplateDO::getName, reqVO.getName())
25 .orderByDesc(MesQcTemplateDO::getId); 25 .orderByDesc(MesQcTemplateDO::getId);
26 if (reqVO.getType() != null) { 26 if (reqVO.getType() != null) {
27 - query.apply(MyBatisUtils.findInSet("types", reqVO.getType())); 27 + query.apply(MyBatisUtils.findInSet("types"), reqVO.getType());
28 } 28 }
29 query.eqIfPresent(MesQcTemplateDO::getStatus, reqVO.getStatus()); 29 query.eqIfPresent(MesQcTemplateDO::getStatus, reqVO.getStatus());
30 return selectPage(reqVO, query); 30 return selectPage(reqVO, query);
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/mail/MailLogMapper.java
@@ -21,7 +21,7 @@ public interface MailLogMapper extends BaseMapperX&lt;MailLogDO&gt; { @@ -21,7 +21,7 @@ public interface MailLogMapper extends BaseMapperX&lt;MailLogDO&gt; {
21 .eqIfPresent(MailLogDO::getSendStatus, reqVO.getSendStatus()) 21 .eqIfPresent(MailLogDO::getSendStatus, reqVO.getSendStatus())
22 .betweenIfPresent(MailLogDO::getSendTime, reqVO.getSendTime()) 22 .betweenIfPresent(MailLogDO::getSendTime, reqVO.getSendTime())
23 .apply(StrUtil.isNotBlank(reqVO.getToMail()), 23 .apply(StrUtil.isNotBlank(reqVO.getToMail()),
24 - MyBatisUtils.findInSet("to_mails", reqVO.getToMail())) 24 + MyBatisUtils.findInSet("to_mails"), reqVO.getToMail())
25 .orderByDesc(MailLogDO::getId)); 25 .orderByDesc(MailLogDO::getId));
26 } 26 }
27 27
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/tenant/TenantMapper.java
@@ -29,7 +29,7 @@ public interface TenantMapper extends BaseMapperX&lt;TenantDO&gt; { @@ -29,7 +29,7 @@ public interface TenantMapper extends BaseMapperX&lt;TenantDO&gt; {
29 29
30 default List<TenantDO> selectListByWebsite(String website) { 30 default List<TenantDO> selectListByWebsite(String website) {
31 return selectList(new LambdaQueryWrapperX<TenantDO>() 31 return selectList(new LambdaQueryWrapperX<TenantDO>()
32 - .apply(MyBatisUtils.findInSet("websites", website))); 32 + .apply(MyBatisUtils.findInSet("websites"), website));
33 } 33 }
34 34
35 default Long selectCountByPackageId(Long packageId) { 35 default Long selectCountByPackageId(Long packageId) {
yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailLogServiceImplTest.java
@@ -160,8 +160,8 @@ public class MailLogServiceImplTest extends BaseDbUnitTest { @@ -160,8 +160,8 @@ public class MailLogServiceImplTest extends BaseDbUnitTest {
160 mailLogMapper.insert(cloneIgnoreId(dbMailLog, o -> o.setUserId(2L))); 160 mailLogMapper.insert(cloneIgnoreId(dbMailLog, o -> o.setUserId(2L)));
161 // 测试 userType 不匹配 161 // 测试 userType 不匹配
162 mailLogMapper.insert(cloneIgnoreId(dbMailLog, o -> o.setUserType(UserTypeEnum.MEMBER.getValue()))); 162 mailLogMapper.insert(cloneIgnoreId(dbMailLog, o -> o.setUserType(UserTypeEnum.MEMBER.getValue())));
163 - // 测试 toMails 不匹配(特殊:find_in_set 无法单测)  
164 -// mailLogMapper.insert(cloneIgnoreId(dbMailLog, o -> o.setToMails(Lists.newArrayList("788@qq.com")))); 163 + // 测试 toMails 不匹配
  164 + mailLogMapper.insert(cloneIgnoreId(dbMailLog, o -> o.setToMails(Lists.newArrayList("788@qq.com"))));
165 // 测试 accountId 不匹配 165 // 测试 accountId 不匹配
166 mailLogMapper.insert(cloneIgnoreId(dbMailLog, o -> o.setAccountId(11L))); 166 mailLogMapper.insert(cloneIgnoreId(dbMailLog, o -> o.setAccountId(11L)));
167 // 测试 templateId 不匹配 167 // 测试 templateId 不匹配
@@ -174,7 +174,7 @@ public class MailLogServiceImplTest extends BaseDbUnitTest { @@ -174,7 +174,7 @@ public class MailLogServiceImplTest extends BaseDbUnitTest {
174 MailLogPageReqVO reqVO = new MailLogPageReqVO(); 174 MailLogPageReqVO reqVO = new MailLogPageReqVO();
175 reqVO.setUserId(1L); 175 reqVO.setUserId(1L);
176 reqVO.setUserType(UserTypeEnum.ADMIN.getValue()); 176 reqVO.setUserType(UserTypeEnum.ADMIN.getValue());
177 -// reqVO.setToMail("768@qq.com"); 177 + reqVO.setToMail("768@qq.com");
178 reqVO.setAccountId(10L); 178 reqVO.setAccountId(10L);
179 reqVO.setTemplateId(100L); 179 reqVO.setTemplateId(100L);
180 reqVO.setSendStatus(MailSendStatusEnum.INIT.getStatus()); 180 reqVO.setSendStatus(MailSendStatusEnum.INIT.getStatus());
yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/tenant/TenantServiceImplTest.java
@@ -22,7 +22,6 @@ import cn.iocoder.yudao.module.system.service.tenant.handler.TenantMenuHandler; @@ -22,7 +22,6 @@ import cn.iocoder.yudao.module.system.service.tenant.handler.TenantMenuHandler;
22 import cn.iocoder.yudao.module.system.service.user.AdminUserService; 22 import cn.iocoder.yudao.module.system.service.user.AdminUserService;
23 import jakarta.annotation.Resource; 23 import jakarta.annotation.Resource;
24 import org.junit.jupiter.api.BeforeEach; 24 import org.junit.jupiter.api.BeforeEach;
25 -import org.junit.jupiter.api.Disabled;  
26 import org.junit.jupiter.api.Test; 25 import org.junit.jupiter.api.Test;
27 import org.springframework.context.annotation.Import; 26 import org.springframework.context.annotation.Import;
28 import org.springframework.test.context.bean.override.mockito.MockitoBean; 27 import org.springframework.test.context.bean.override.mockito.MockitoBean;
@@ -334,7 +333,6 @@ public class TenantServiceImplTest extends BaseDbUnitTest { @@ -334,7 +333,6 @@ public class TenantServiceImplTest extends BaseDbUnitTest {
334 } 333 }
335 334
336 @Test 335 @Test
337 - @Disabled // H2 不支持 find_in_set 函数  
338 public void testGetTenantByWebsite() { 336 public void testGetTenantByWebsite() {
339 // mock 数据 337 // mock 数据
340 TenantDO dbTenant = randomPojo(TenantDO.class, o -> o.setWebsites(singletonList("https://www.iocoder.cn"))); 338 TenantDO dbTenant = randomPojo(TenantDO.class, o -> o.setWebsites(singletonList("https://www.iocoder.cn")));