diff --git a/cn-universal-admin/src/main/java/cn/universal/admin/system/service/ISysMenuService.java b/cn-universal-admin/src/main/java/cn/universal/admin/system/service/ISysMenuService.java index 850bc7d..499564e 100644 --- a/cn-universal-admin/src/main/java/cn/universal/admin/system/service/ISysMenuService.java +++ b/cn-universal-admin/src/main/java/cn/universal/admin/system/service/ISysMenuService.java @@ -142,10 +142,4 @@ public interface ISysMenuService { */ public String checkMenuNameUnique(SysMenu menu); - /** - * 执行菜单脚本 - * - * @param sql 菜单脚本 - */ - void execMenuSql(String sql); } diff --git a/cn-universal-admin/src/main/java/cn/universal/admin/system/service/impl/SysMenuServiceImpl.java b/cn-universal-admin/src/main/java/cn/universal/admin/system/service/impl/SysMenuServiceImpl.java index 20918b5..95812d8 100644 --- a/cn-universal-admin/src/main/java/cn/universal/admin/system/service/impl/SysMenuServiceImpl.java +++ b/cn-universal-admin/src/main/java/cn/universal/admin/system/service/impl/SysMenuServiceImpl.java @@ -453,13 +453,4 @@ public class SysMenuServiceImpl implements ISysMenuService { return getChildList(list, t).size() > 0; } - /** - * 执行菜单脚本 - * - * @param sql 菜单脚本 - */ - @Override - public void execMenuSql(String sql) { - menuMapper.execMenuSql(sql); - } } diff --git a/cn-universal-data-bridge/cn-universal-data-bridge-core/src/main/java/cn/universal/databridge/engine/ParamTemplateEngine.java b/cn-universal-data-bridge/cn-universal-data-bridge-core/src/main/java/cn/universal/databridge/engine/ParamTemplateEngine.java index 4d212b6..8c3b26d 100644 --- a/cn-universal-data-bridge/cn-universal-data-bridge-core/src/main/java/cn/universal/databridge/engine/ParamTemplateEngine.java +++ b/cn-universal-data-bridge/cn-universal-data-bridge-core/src/main/java/cn/universal/databridge/engine/ParamTemplateEngine.java @@ -2,53 +2,107 @@ package cn.universal.databridge.engine; import cn.hutool.core.util.StrUtil; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; -/** - * 参数化模板引擎:支持 #{path} 值占位 - * 所有值均以 '?' 参数占位绑定,避免引号与转义问题 - */ +/** 参数化模板引擎:支持 #{path} 值占位 所有值均以 '?' 参数占位绑定,避免引号与转义问题 */ public class ParamTemplateEngine { - private static final Pattern VAR_PATTERN = Pattern.compile("#\\{([a-zA-Z0-9_.]+)\\}"); + private static final Pattern TEMPLATE_PATTERN = + Pattern.compile("#\\{([a-zA-Z0-9_.]+)}|\\$\\{(json|json_text)\\((.*?)\\)}", Pattern.DOTALL); - public ParamSql process(String template, Map variables, SqlDialectAdapter adapter) { + public ParamSql process( + String template, Map variables, SqlDialectAdapter adapter) { if (StrUtil.isBlank(template)) { return new ParamSql("", List.of()); } - String sql = template; List params = new ArrayList<>(); - - // 处理 #{var.path} 占位符 - Matcher m = VAR_PATTERN.matcher(sql); + Matcher m = TEMPLATE_PATTERN.matcher(template); StringBuffer sb = new StringBuffer(); while (m.find()) { - String path = m.group(1); - Object value = getNestedValue(variables, path); - - // 将值转换为适合数据库的格式 - Object paramValue = convertValueForDatabase(value, path); - params.add(paramValue); - - // properties 和 data 字段如果是 Map 序列化的 JSON 字符串,需要特殊处理 - String placeholder = "?"; - if (("properties".equals(path) || "data".equals(path)) && paramValue instanceof String) { - String strValue = (String) paramValue; - // 检查是否是 JSON 对象格式(以 { 开头且以 } 结尾) - if (strValue.trim().startsWith("{") && strValue.trim().endsWith("}")) { - placeholder = adapter.wrapJsonParameter(); - } + String varPath = m.group(1); + String macroName = m.group(2); + String replacement; + if (varPath != null) { + Object paramValue = convertValueForDatabase(getNestedValue(variables, varPath), varPath); + params.add(paramValue); + replacement = jsonParameterPlaceholder(varPath, paramValue, adapter); + } else { + replacement = processJsonMacro(macroName, m.group(3), variables, adapter, params); } - m.appendReplacement(sb, Matcher.quoteReplacement(placeholder)); + m.appendReplacement(sb, Matcher.quoteReplacement(replacement)); } m.appendTail(sb); - sql = sb.toString(); + return new ParamSql(sb.toString(), params); + } - return new ParamSql(sql, params); + private String processJsonMacro( + String macroName, + String argumentsText, + Map variables, + SqlDialectAdapter adapter, + List params) { + List arguments = splitArguments(argumentsText); + if (arguments.isEmpty() || arguments.size() % 2 != 0) { + throw new IllegalArgumentException("invalid " + macroName + " macro arguments: " + argumentsText); + } + List keys = new ArrayList<>(arguments.size() / 2); + for (int index = 0; index < arguments.size(); index += 2) { + keys.add(unquote(arguments.get(index))); + String valuePath = arguments.get(index + 1).trim(); + params.add(convertValueForDatabase(getNestedValue(variables, valuePath), valuePath)); + } + if ("json_text".equals(macroName)) { + return adapter.jsonTextFragment(keys, keys.size()); + } + return adapter.jsonFragment(keys, keys.size()); + } + + private List splitArguments(String text) { + List result = new ArrayList<>(); + if (text == null || text.isBlank()) { + return result; + } + StringBuilder current = new StringBuilder(); + boolean inSingleQuote = false; + boolean inDoubleQuote = false; + for (int index = 0; index < text.length(); index++) { + char ch = text.charAt(index); + if (ch == '\'' && !inDoubleQuote) { + inSingleQuote = !inSingleQuote; + current.append(ch); + continue; + } + if (ch == '"' && !inSingleQuote) { + inDoubleQuote = !inDoubleQuote; + current.append(ch); + continue; + } + if (ch == ',' && !inSingleQuote && !inDoubleQuote) { + result.add(current.toString().trim()); + current.setLength(0); + continue; + } + current.append(ch); + } + if (!current.isEmpty()) { + result.add(current.toString().trim()); + } + return result; + } + + private String unquote(String value) { + String trimmed = value == null ? "" : value.trim(); + if (trimmed.length() >= 2) { + char first = trimmed.charAt(0); + char last = trimmed.charAt(trimmed.length() - 1); + if ((first == '\'' && last == '\'') || (first == '"' && last == '"')) { + return trimmed.substring(1, trimmed.length() - 1).replace("\\'", "'").replace("\\\"", "\""); + } + } + return trimmed; } @SuppressWarnings("unchecked") @@ -71,8 +125,19 @@ public class ParamTemplateEngine { return curr; } + private String jsonParameterPlaceholder(String path, Object paramValue, SqlDialectAdapter adapter) { + if (("properties".equals(path) || "data".equals(path) || "rawData".equals(path)) + && paramValue instanceof String strValue + && strValue.trim().startsWith("{") + && strValue.trim().endsWith("}")) { + return adapter.wrapJsonParameter(); + } + return "?"; + } + /** * 将值转换为适合数据库存储的格式 + * * @param value 原始值 * @param path 变量路径 * @return 转换后的值 @@ -81,26 +146,20 @@ public class ParamTemplateEngine { if (value == null) { return null; } - - // 如果已经是字符串,直接返回 - if (value instanceof String) { - return value; - } - - // 其他类型转为字符串,避免类型不匹配问题 - if (value instanceof Number || value instanceof Boolean) { - return value.toString(); - } - + if (value instanceof java.util.Date) { return new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(value); } - + + if (value instanceof Number || value instanceof Boolean || value instanceof String) { + return value; + } + // Map 类型序列化为 JSON if (value instanceof Map) { return cn.hutool.json.JSONUtil.toJsonStr(value); } - + // 其他类型默认转为字符串 return value.toString(); } diff --git a/cn-universal-data-bridge/cn-universal-data-bridge-core/src/main/java/cn/universal/databridge/util/SqlTemplateProcessor.java b/cn-universal-data-bridge/cn-universal-data-bridge-core/src/main/java/cn/universal/databridge/util/SqlTemplateProcessor.java deleted file mode 100644 index 0258082..0000000 --- a/cn-universal-data-bridge/cn-universal-data-bridge-core/src/main/java/cn/universal/databridge/util/SqlTemplateProcessor.java +++ /dev/null @@ -1,169 +0,0 @@ -/* - * - * Copyright (c) 2025, NexIoT. All Rights Reserved. - * - * @Description: SQL模板处理器 - * @Author: gitee.com/NexIoT - * @Email: wo8335224@gmail.com - * @Wechat: outlookFil - * - */ - -package cn.universal.databridge.util; - -import cn.hutool.json.JSONUtil; -import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import lombok.extern.slf4j.Slf4j; - -/** - * SQL模板处理器 - * 智能处理SQL模板中的变量替换,避免引号冲突 - * - * @author gitee.com/NexIoT - * @version 1.0 - * @since 2025/1/15 - */ -@Slf4j -public class SqlTemplateProcessor { - - /** - * 处理SQL模板,智能处理引号冲突 - * - * @param template SQL模板 - * @param variables 变量映射 - * @return 处理后的SQL - */ - public static String processTemplate(String template, Map variables) { - if (template == null || template.trim().isEmpty()) { - return ""; - } - - // 使用正则表达式匹配所有 #{...} 占位符 - Pattern pattern = Pattern.compile("#\\{([a-zA-Z0-9_.]+)\\}"); - Matcher matcher = pattern.matcher(template); - - StringBuffer result = new StringBuffer(); - - while (matcher.find()) { - String paramPath = matcher.group(1); - Object value = getNestedValue(variables, paramPath); - String replacement = convertToSqlValue(value, template, matcher.start()); - matcher.appendReplacement(result, Matcher.quoteReplacement(replacement)); - } - matcher.appendTail(result); - - return result.toString(); - } - - /** - * 智能转换为SQL值,根据上下文选择合适的引号 - * - * @param value 要转换的值 - * @param template 完整模板 - * @param position 当前位置 - * @return SQL安全格式的字符串 - */ - private static String convertToSqlValue(Object value, String template, int position) { - if (value == null) { - return "NULL"; - } - - // 检查上下文,判断是否在JSON字符串中 - boolean inJsonContext = isInJsonContext(template, position); - - if (inJsonContext) { - // 在JSON上下文中,使用双引号 - return convertToJsonValue(value); - } else { - // 在普通SQL上下文中,使用单引号 - return SqlValueConverter.convertToSqlValue(value); - } - } - - /** - * 检查当前位置是否在JSON字符串上下文中 - * - * @param template 模板 - * @param position 位置 - * @return 是否在JSON上下文中 - */ - private static boolean isInJsonContext(String template, int position) { - // 向前查找最近的引号 - int singleQuotePos = template.lastIndexOf("'", position); - int doubleQuotePos = template.lastIndexOf("\"", position); - - // 如果最近的是双引号,说明在JSON上下文中 - if (doubleQuotePos > singleQuotePos) { - return true; - } - - // 检查是否在JSON对象中(包含大括号) - String beforePosition = template.substring(0, position); - int lastOpenBrace = beforePosition.lastIndexOf("{"); - int lastCloseBrace = beforePosition.lastIndexOf("}"); - - // 如果最近的大括号是开括号,说明在JSON对象中 - if (lastOpenBrace > lastCloseBrace) { - return true; - } - - return false; - } - - /** - * 转换为JSON值 - * - * @param value 要转换的值 - * @return JSON格式的字符串 - */ - private static String convertToJsonValue(Object value) { - if (value == null) { - return "null"; - } - - if (value instanceof String) { - // 字符串:转义双引号 - String strValue = (String) value; - return "\"" + strValue.replace("\"", "\\\"") + "\""; - } else if (value instanceof Number || value instanceof Boolean) { - // 数字和布尔类型直接转换 - return value.toString(); - } else { - // 其他类型:转为JSON字符串 - return JSONUtil.toJsonStr(value); - } - } - - /** - * 获取嵌套属性值 - * - * @param variables 变量映射 - * @param path 属性路径 - * @return 属性值 - */ - @SuppressWarnings("unchecked") - private static Object getNestedValue(Map variables, String path) { - if (path == null || path.trim().isEmpty()) { - return null; - } - - String[] keys = path.split("\\."); - Object current = variables; - - for (String key : keys) { - if (current == null) { - return null; - } - - if (current instanceof Map) { - current = ((Map) current).get(key); - } else { - return null; - } - } - - return current; - } -} diff --git a/cn-universal-data-bridge/cn-universal-data-bridge-core/src/main/java/cn/universal/databridge/util/SqlValueConverter.java b/cn-universal-data-bridge/cn-universal-data-bridge-core/src/main/java/cn/universal/databridge/util/SqlValueConverter.java deleted file mode 100644 index 44e4de0..0000000 --- a/cn-universal-data-bridge/cn-universal-data-bridge-core/src/main/java/cn/universal/databridge/util/SqlValueConverter.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * - * Copyright (c) 2025, NexIoT. All Rights Reserved. - * - * @Description: 本文件由 gitee.com/NexIoT 开发并拥有版权,未经授权严禁擅自商用、复制或传播。 - * @Author: gitee.com/NexIoT - * @Email: wo8335224@gmail.com - * @Wechat: outlookFil - * - * - */ - -package cn.universal.databridge.util; - -import cn.hutool.json.JSONUtil; -import lombok.extern.slf4j.Slf4j; - -/** - * SQL值转换工具类 - * 提供将Java对象转换为SQL安全格式的通用方法 - * - * @author gitee.com/NexIoT - * @version 1.0 - * @since 2025/1/15 - */ -@Slf4j -public class SqlValueConverter { - - /** - * 将变量值转换为SQL安全格式 - * 1. 字符串类型:添加单引号,转义内部单引号 - * 2. 数字/布尔类型:直接转换为字符串 - * 3. null:转换为SQL的NULL关键字 - * 4. 日期类型:转换为SQL标准格式 - * 5. 其他类型:转为JSON字符串 - * - * @param value 要转换的值 - * @return SQL安全格式的字符串 - */ - public static String convertToSqlValue(Object value) { - if (value == null) { - return "NULL"; - } - - if (value instanceof String) { - // 转义单引号(防止SQL注入和语法错误) - String strValue = (String) value; - return "'" + strValue.replace("'", "''") + "'"; - } else if (value instanceof Number || value instanceof Boolean) { - // 数字和布尔类型直接转换 - return value.toString(); - } else if (value instanceof java.util.Date) { - // 日期类型转换为SQL标准格式(带单引号) - return "'" + new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(value) + "'"; - } else { - // 其他类型:转为JSON字符串(带单引号) - String jsonValue = JSONUtil.toJsonStr(value); - return "'" + jsonValue.replace("'", "''") + "'"; - } - } - - /** - * 批量转换多个值为SQL安全格式 - * - * @param values 要转换的值数组 - * @return SQL安全格式的字符串数组 - */ - public static String[] convertToSqlValues(Object... values) { - if (values == null) { - return new String[0]; - } - - String[] result = new String[values.length]; - for (int i = 0; i < values.length; i++) { - result[i] = convertToSqlValue(values[i]); - } - return result; - } - - /** - * 将变量值转换为SQL安全格式(JSON字符串专用) - * 使用双引号包围JSON字符串,避免单引号冲突 - * - * @param value 要转换的值 - * @return SQL安全格式的字符串 - */ - public static String convertToSqlJsonValue(Object value) { - if (value == null) { - return "NULL"; - } - - if (value instanceof String) { - // JSON字符串:使用双引号包围,转义内部双引号 - String strValue = (String) value; - return "\"" + strValue.replace("\"", "\\\"") + "\""; - } else if (value instanceof Number || value instanceof Boolean) { - // 数字和布尔类型直接转换 - return value.toString(); - } else if (value instanceof java.util.Date) { - // 日期类型转换为SQL标准格式(带双引号) - return "\"" + new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(value) + "\""; - } else { - // 其他类型:转为JSON字符串(带双引号) - String jsonValue = JSONUtil.toJsonStr(value); - return "\"" + jsonValue.replace("\"", "\\\"") + "\""; - } - } - - /** - * 检查值是否需要SQL转义 - * - * @param value 要检查的值 - * @return 是否需要转义 - */ - public static boolean needsEscaping(Object value) { - if (value == null) { - return false; - } - - if (value instanceof String) { - String strValue = (String) value; - return strValue.contains("'") || strValue.contains("\\") || strValue.contains("\""); - } - - return !(value instanceof Number || value instanceof Boolean); - } -} diff --git a/cn-universal-data-bridge/cn-universal-data-bridge-core/src/test/java/cn/universal/databridge/engine/ParamTemplateEngineTest.java b/cn-universal-data-bridge/cn-universal-data-bridge-core/src/test/java/cn/universal/databridge/engine/ParamTemplateEngineTest.java index 953dc4e..f03c0d1 100644 --- a/cn-universal-data-bridge/cn-universal-data-bridge-core/src/test/java/cn/universal/databridge/engine/ParamTemplateEngineTest.java +++ b/cn-universal-data-bridge/cn-universal-data-bridge-core/src/test/java/cn/universal/databridge/engine/ParamTemplateEngineTest.java @@ -1,17 +1,17 @@ package cn.universal.databridge.engine; -import cn.universal.databridge.engine.dialect.MySqlDialectAdapter; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.util.HashMap; -import java.util.Map; - import static org.junit.jupiter.api.Assertions.*; -/** - * ParamTemplateEngine 单元测试 - */ +import cn.universal.databridge.engine.dialect.MySqlDialectAdapter; +import cn.universal.databridge.engine.dialect.OracleDialectAdapter; +import cn.universal.databridge.engine.dialect.PostgresDialectAdapter; +import cn.universal.databridge.engine.dialect.SqlServerDialectAdapter; +import java.util.HashMap; +import java.util.Map; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** ParamTemplateEngine 单元测试 */ class ParamTemplateEngineTest { private ParamTemplateEngine engine; @@ -40,27 +40,98 @@ class ParamTemplateEngineTest { @Test void testMysqlTemplate() { - SqlDialectAdapter adapter = new MySqlDialectAdapter(); + SqlDialectAdapter adapter = new MySqlDialectAdapter(); - String template = "INSERT INTO device_data(\n" + - " device_id, product_key, device_name, iot_id,\n" + - " device_node, message_type, properties, raw_data\n" + - ") VALUES(\n" + - " #{deviceId}, #{productKey}, #{deviceName}, #{iotId},\n" + - " #{deviceNode}, #{messageType}, #{properties}, \n" + - " ${json('csq', properties.temperature, 'tips', properties.illuminationDesc)}\n" + - ");"; + String template = + "INSERT INTO device_data(\n" + + " device_id, product_key, device_name, iot_id,\n" + + " device_node, message_type, properties, raw_data\n" + + ") VALUES(\n" + + " #{deviceId}, #{productKey}, #{deviceName}, #{iotId},\n" + + " #{deviceNode}, #{messageType}, #{properties}, \n" + + " ${json('csq', properties.temperature, 'tips', properties.illuminationDesc)}\n" + + ");"; - ParamSql result = engine.process(template, variables, adapter); + ParamSql result = engine.process(template, variables, adapter); - assertNotNull(result); - assertTrue(result.getSql().contains("JSON_OBJECT")); - assertTrue(result.getSql().contains("?")); - assertEquals(9, result.getParams().size()); - assertEquals("dev123", result.getParams().get(0)); - assertEquals("pk001", result.getParams().get(1)); - assertEquals(25.5, result.getParams().get(3)); // csq value - assertEquals("明亮", result.getParams().get(4)); // tips value + assertNotNull(result); + assertTrue(result.getSql().contains("JSON_OBJECT")); + assertTrue(result.getSql().contains("?")); + assertEquals(9, result.getParams().size()); + assertEquals("dev123", result.getParams().get(0)); + assertEquals("pk001", result.getParams().get(1)); + assertEquals(25.5, result.getParams().get(7)); // csq value + assertEquals("明亮", result.getParams().get(8)); // tips value + } + + @Test + void testPostgreSqlTemplate() { + SqlDialectAdapter adapter = new PostgresDialectAdapter(); + + String template = + "INSERT INTO device_data(\n" + + " device_id, product_key, device_name, iot_id,\n" + + " device_node, message_type, properties, raw_data\n" + + ") VALUES(\n" + + " #{deviceId}, #{productKey}, #{deviceName}, #{iotId},\n" + + " #{deviceNode}, #{messageType}, #{properties}, \n" + + " ${json_text('csq', properties.temperature, 'tips', properties.illuminationDesc)}\n" + + ");"; + + ParamSql result = engine.process(template, variables, adapter); + + assertNotNull(result); + assertTrue(result.getSql().contains("json_build_object")); + assertTrue(result.getSql().contains("::TEXT")); + assertTrue(result.getSql().contains("?")); + assertEquals(9, result.getParams().size()); + assertEquals("dev123", result.getParams().get(0)); + assertEquals("明亮", result.getParams().get(8)); + } + + @Test + void testOracleTemplate() { + SqlDialectAdapter adapter = new OracleDialectAdapter(); + + String template = + "INSERT INTO device_data(\n" + + " device_id, product_key, device_name, iot_id,\n" + + " device_node, message_type, properties, raw_data\n" + + ") VALUES(\n" + + " #{deviceId}, #{productKey}, #{deviceName}, #{iotId},\n" + + " #{deviceNode}, #{messageType}, #{properties}, \n" + + " ${json('csq', properties.temperature, 'tips', properties.illuminationDesc)}\n" + + ");"; + + ParamSql result = engine.process(template, variables, adapter); + + assertNotNull(result); + assertTrue(result.getSql().contains("JSON_OBJECT")); + assertTrue(result.getSql().contains("VALUE")); + assertTrue(result.getSql().contains("?")); + assertEquals(9, result.getParams().size()); + } + + @Test + void testSqlServerTemplate() { + SqlDialectAdapter adapter = new SqlServerDialectAdapter(); + + String template = + "INSERT INTO device_data(\n" + + " device_id, product_key, device_name, iot_id,\n" + + " device_node, message_type, properties, raw_data\n" + + ") VALUES(\n" + + " #{deviceId}, #{productKey}, #{deviceName}, #{iotId},\n" + + " #{deviceNode}, #{messageType}, #{properties}, \n" + + " ${json('csq', properties.temperature, 'tips', properties.illuminationDesc)}\n" + + ");"; + + ParamSql result = engine.process(template, variables, adapter); + + assertNotNull(result); + assertTrue(result.getSql().contains("CONCAT")); + assertTrue(result.getSql().contains("?")); + assertEquals(9, result.getParams().size()); } @Test @@ -140,18 +211,50 @@ class ParamTemplateEngineTest { assertEquals(template, result.getSql()); assertTrue(result.getParams().isEmpty()); } + @Test + void testMixedJsonAndJsonText() { + SqlDialectAdapter adapter = new PostgresDialectAdapter(); + String template = + "INSERT INTO test(col1, col2) VALUES(" + + "${json('temp', properties.temperature)}, " + + "${json_text('desc', properties.illuminationDesc)}" + + ")"; - @Test - void testChineseCharacters() { - SqlDialectAdapter adapter = new MySqlDialectAdapter(); + ParamSql result = engine.process(template, variables, adapter); - variables.put("chineseValue", "测试中文"); - String template = "INSERT INTO test(name) VALUES(#{chineseValue})"; + assertTrue(result.getSql().contains("json_build_object")); + assertTrue(result.getSql().contains("::TEXT")); + assertEquals(2, result.getParams().size()); + assertEquals(25.5, result.getParams().get(0)); + assertEquals("明亮", result.getParams().get(1)); + } - ParamSql result = engine.process(template, variables, adapter); + @Test + void testChineseCharacters() { + SqlDialectAdapter adapter = new MySqlDialectAdapter(); - assertEquals("INSERT INTO test(name) VALUES(?)", result.getSql()); - assertEquals("测试中文", result.getParams().get(0)); - } + variables.put("chineseValue", "测试中文"); + String template = "INSERT INTO test(name) VALUES(#{chineseValue})"; + + ParamSql result = engine.process(template, variables, adapter); + + assertEquals("INSERT INTO test(name) VALUES(?)", result.getSql()); + assertEquals("测试中文", result.getParams().get(0)); + } + + @Test + void testSpecialCharactersInJson() { + SqlDialectAdapter adapter = new PostgresDialectAdapter(); + + Map props = (Map) variables.get("properties"); + props.put("specialDesc", "包含\"引号\"和'单引号'"); + + String template = "SELECT ${json_text('data', properties.specialDesc)}"; + + ParamSql result = engine.process(template, variables, adapter); + + assertNotNull(result); + assertTrue(result.getParams().get(0).toString().contains("引号")); + } } diff --git a/cn-universal-data-bridge/cn-universal-data-bridge-plugins/cn-universal-data-bridge-plugin-jdbc/src/main/java/cn/universal/databridge/plugin/jdbc/DefaultJdbcOutPlugin.java b/cn-universal-data-bridge/cn-universal-data-bridge-plugins/cn-universal-data-bridge-plugin-jdbc/src/main/java/cn/universal/databridge/plugin/jdbc/DefaultJdbcOutPlugin.java index f39fe32..d48c827 100644 --- a/cn-universal-data-bridge/cn-universal-data-bridge-plugins/cn-universal-data-bridge-plugin-jdbc/src/main/java/cn/universal/databridge/plugin/jdbc/DefaultJdbcOutPlugin.java +++ b/cn-universal-data-bridge/cn-universal-data-bridge-plugins/cn-universal-data-bridge-plugin-jdbc/src/main/java/cn/universal/databridge/plugin/jdbc/DefaultJdbcOutPlugin.java @@ -1,12 +1,15 @@ package cn.universal.databridge.plugin.jdbc; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONObject; +import cn.universal.databridge.engine.ParamSql; +import cn.universal.databridge.engine.ParamTemplateEngine; import cn.universal.databridge.entity.DataBridgeConfig; import cn.universal.databridge.entity.PluginInfo; import cn.universal.databridge.entity.ResourceConnection; import cn.universal.databridge.plugin.AbstractDataOutputPlugin; import cn.universal.databridge.plugin.SourceScope; import cn.universal.databridge.util.DataBridgeConnectionManager; -import cn.universal.databridge.util.SqlValueConverter; import cn.universal.persistence.base.BaseUPRequest; import java.sql.Connection; import java.util.ArrayList; @@ -76,6 +79,25 @@ public class DefaultJdbcOutPlugin extends AbstractDataOutputPlugin { SourceScope.ALL_PRODUCTS, SourceScope.SPECIFIC_PRODUCTS, SourceScope.APPLICATION); } + @Override + protected void processWithDefaultTemplate( + List requests, + DataBridgeConfig config, + ResourceConnection connection, + JSONObject configJson) { + try { + DataSource dataSource = connectionManager.getOrCreateDataSource(connection); + JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); + for (BaseUPRequest request : requests) { + Map variables = buildTemplateVariables(request, configJson); + executeParamSql(jdbcTemplate, config, variables, connection); + } + } catch (Exception e) { + connectionManager.removeDataSource(connection); + throw new RuntimeException("SQL执行失败: " + e.getMessage(), e); + } + } + @Override protected void processProcessedData( Object processedData, @@ -86,12 +108,9 @@ public class DefaultJdbcOutPlugin extends AbstractDataOutputPlugin { DataSource dataSource = connectionManager.getOrCreateDataSource(connection); JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); - List sqlList = generateExecutableSqlList(processedData, config); - - if (sqlList.size() > 1) { - jdbcTemplate.batchUpdate(sqlList.toArray(new String[0])); - } else if (sqlList.size() == 1) { - jdbcTemplate.update(sqlList.get(0)); + List sqlList = generateExecutableSqlList(processedData, config, connection); + for (ParamSql sql : sqlList) { + executeParamSql(jdbcTemplate, sql); } } catch (Exception e) { @@ -108,15 +127,16 @@ public class DefaultJdbcOutPlugin extends AbstractDataOutputPlugin { ResourceConnection connection) { try { javax.sql.DataSource dataSource = connectionManager.getOrCreateDataSource(connection); - org.springframework.jdbc.core.JdbcTemplate jdbcTemplate = new org.springframework.jdbc.core.JdbcTemplate(dataSource); + org.springframework.jdbc.core.JdbcTemplate jdbcTemplate = + new org.springframework.jdbc.core.JdbcTemplate(dataSource); // 构建变量并进行参数化模板解析(避免引号问题,兼容多方言) java.util.Map variables = buildTemplateVariables(request, parseConfig(config)); - cn.universal.databridge.engine.ParamTemplateEngine engine = new cn.universal.databridge.engine.ParamTemplateEngine(); + ParamTemplateEngine engine = new ParamTemplateEngine(); cn.universal.databridge.engine.SqlDialectAdapter adapter = getAdapter(connection.getType()); - cn.universal.databridge.engine.ParamSql ps = engine.process(config.getTemplate(), variables, adapter); + ParamSql ps = engine.process(config.getTemplate(), variables, adapter); - jdbcTemplate.update(ps.getSql(), ps.getParams().toArray()); + executeParamSql(jdbcTemplate, ps); } catch (Exception e) { log.error("执行SQL失败: {} - {}", templateResult, e.getMessage()); connectionManager.removeDataSource(connection); @@ -124,7 +144,8 @@ public class DefaultJdbcOutPlugin extends AbstractDataOutputPlugin { } } - private cn.universal.databridge.engine.SqlDialectAdapter getAdapter(cn.universal.databridge.entity.ResourceConnection.ResourceType type) { + private cn.universal.databridge.engine.SqlDialectAdapter getAdapter( + cn.universal.databridge.entity.ResourceConnection.ResourceType type) { switch (type) { case MYSQL: case H2: @@ -140,28 +161,29 @@ public class DefaultJdbcOutPlugin extends AbstractDataOutputPlugin { } } - private List generateExecutableSqlList(Object processedData, DataBridgeConfig config) { - List sqlList = new ArrayList<>(); + private List generateExecutableSqlList( + Object processedData, DataBridgeConfig config, ResourceConnection connection) { + List sqlList = new ArrayList<>(); try { if (processedData instanceof String) { - sqlList.add((String) processedData); + throw new IllegalArgumentException("JDBC输出Magic脚本不允许直接返回SQL字符串"); } else if (processedData instanceof List) { @SuppressWarnings("unchecked") List dataList = (List) processedData; for (Object item : dataList) { if (item instanceof String) { - sqlList.add((String) item); + throw new IllegalArgumentException("JDBC输出Magic脚本不允许直接返回SQL字符串"); } else if (item instanceof Map) { @SuppressWarnings("unchecked") Map dataMap = (Map) item; - sqlList.add(processTemplate(config.getTemplate(), dataMap)); + sqlList.add(buildParamSql(config, dataMap, connection)); } } } else if (processedData instanceof Map) { @SuppressWarnings("unchecked") Map dataMap = (Map) processedData; - sqlList.add(processTemplate(config.getTemplate(), dataMap)); + sqlList.add(buildParamSql(config, dataMap, connection)); } else { log.warn( "Magic脚本返回的数据类型不支持: {}", @@ -176,128 +198,24 @@ public class DefaultJdbcOutPlugin extends AbstractDataOutputPlugin { return sqlList; } - // 使用正则表达式匹配所有 #{...} 占位符,支持嵌套属性 - // 智能处理引号,兼容MySQL和PostgreSQL - protected String processTemplate(String template, Map variables) { - if (template == null || template.trim().isEmpty()) { - log.warn("模板为空,无法处理"); - return ""; - } - String result = template; - java.util.regex.Pattern pattern = java.util.regex.Pattern.compile("#\\{([a-zA-Z0-9_.]+)\\}"); - java.util.regex.Matcher matcher = pattern.matcher(template); - StringBuffer sb = new StringBuffer(); - while (matcher.find()) { - String paramPath = matcher.group(1); // 获取变量路径,如 "properties.csq" - Object value = getNestedValue(variables, paramPath); - - // 检测占位符在模板中的上下文,判断是否已经在引号内 - int start = matcher.start(); - int end = matcher.end(); - boolean inSingleQuotes = isInQuotes(template, start, end, '\''); - boolean inDoubleQuotes = isInQuotes(template, start, end, '"'); - - String sqlValue; - if (inSingleQuotes) { - // 如果已经在单引号内,只替换值,不添加引号,但需要转义单引号 - sqlValue = convertValueForQuotedContext(value, true); - } else if (inDoubleQuotes) { - // 如果已经在双引号内,只替换值,不添加引号,但需要转义双引号 - sqlValue = convertValueForQuotedContext(value, false); - } else { - // 如果不在引号内,使用标准的SQL值转换(添加引号) - sqlValue = SqlValueConverter.convertToSqlValue(value); - } - - matcher.appendReplacement(sb, java.util.regex.Matcher.quoteReplacement(sqlValue)); - } - matcher.appendTail(sb); - result = sb.toString(); - - return result; + private ParamSql buildParamSql( + DataBridgeConfig config, Map variables, ResourceConnection connection) { + ParamTemplateEngine engine = new ParamTemplateEngine(); + return engine.process(config.getTemplate(), variables, getAdapter(connection.getType())); } - /** - * 检测占位符是否在引号内 - * @param template 模板字符串 - * @param start 占位符开始位置 - * @param end 占位符结束位置 - * @param quoteChar 引号字符(' 或 ") - * @return 是否在引号内 - */ - private boolean isInQuotes(String template, int start, int end, char quoteChar) { - // 向前查找最近的未转义引号 - int quoteBefore = -1; - for (int i = start - 1; i >= 0; i--) { - if (template.charAt(i) == quoteChar) { - // 检查是否被转义(考虑连续的反斜杠) - int backslashCount = 0; - for (int j = i - 1; j >= 0 && template.charAt(j) == '\\'; j--) { - backslashCount++; - } - // 如果反斜杠数量是偶数,说明引号未被转义 - if (backslashCount % 2 == 0) { - quoteBefore = i; - break; - } - } - } - - if (quoteBefore == -1) { - return false; - } - - // 向后查找匹配的未转义引号 - int quoteAfter = -1; - for (int i = end; i < template.length(); i++) { - if (template.charAt(i) == quoteChar) { - // 检查是否被转义 - int backslashCount = 0; - for (int j = i - 1; j >= 0 && template.charAt(j) == '\\'; j--) { - backslashCount++; - } - // 如果反斜杠数量是偶数,说明引号未被转义 - if (backslashCount % 2 == 0) { - quoteAfter = i; - break; - } - } - } - - // 如果找到了匹配的引号对,说明在引号内 - return quoteAfter != -1 && quoteBefore < start && quoteAfter >= end; + private void executeParamSql( + JdbcTemplate jdbcTemplate, + DataBridgeConfig config, + Map variables, + ResourceConnection connection) { + executeParamSql(jdbcTemplate, buildParamSql(config, variables, connection)); } - /** - * 为已在引号内的上下文转换值 - * @param value 要转换的值 - * @param isSingleQuote 是否是单引号上下文 - * @return 转换后的值(不包含引号,但已转义) - */ - private String convertValueForQuotedContext(Object value, boolean isSingleQuote) { - if (value == null) { - return "NULL"; - } - - String quoteChar = isSingleQuote ? "'" : "\""; - String escapeChar = isSingleQuote ? "''" : "\\\""; - - if (value instanceof String) { - String strValue = (String) value; - // 转义引号 - return strValue.replace(quoteChar, escapeChar); - } else if (value instanceof Number || value instanceof Boolean) { - // 数字和布尔类型直接转换 - return value.toString(); - } else if (value instanceof java.util.Date) { - // 日期类型转换为SQL标准格式 - return new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(value); - } else { - // 其他类型:转为JSON字符串并转义引号 - String jsonValue = cn.hutool.json.JSONUtil.toJsonStr(value); - return jsonValue.replace(quoteChar, escapeChar); + private void executeParamSql(JdbcTemplate jdbcTemplate, ParamSql sql) { + if (sql == null || StrUtil.isBlank(sql.getSql())) { + return; } + jdbcTemplate.update(sql.getSql(), sql.getParams().toArray()); } - - } diff --git a/cn-universal-data-bridge/cn-universal-data-bridge-plugins/cn-universal-data-bridge-plugin-jdbc/src/test/java/cn/universal/databridge/plugin/jdbc/DefaultJdbcOutPluginQuoteTest.java b/cn-universal-data-bridge/cn-universal-data-bridge-plugins/cn-universal-data-bridge-plugin-jdbc/src/test/java/cn/universal/databridge/plugin/jdbc/DefaultJdbcOutPluginQuoteTest.java deleted file mode 100644 index e6e8528..0000000 --- a/cn-universal-data-bridge/cn-universal-data-bridge-plugins/cn-universal-data-bridge-plugin-jdbc/src/test/java/cn/universal/databridge/plugin/jdbc/DefaultJdbcOutPluginQuoteTest.java +++ /dev/null @@ -1,305 +0,0 @@ -package cn.universal.databridge.plugin.jdbc; - -import static org.junit.jupiter.api.Assertions.*; - -import java.util.HashMap; -import java.util.Map; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -/** - * DefaultJdbcOutPlugin 智能引号处理测试 - * 测试兼容MySQL和PostgreSQL的引号处理功能 - * - * @author gitee.com/NexIoT - * @version 1.0 - * @since 2025/1/15 - */ -@DisplayName("JDBC插件智能引号处理测试") -public class DefaultJdbcOutPluginQuoteTest { - - private DefaultJdbcOutPlugin plugin; - private Map variables; - - @BeforeEach - void setUp() { - plugin = new DefaultJdbcOutPlugin(); - - // 准备测试数据 - Map properties = new HashMap<>(); - properties.put("temperature", 14.3); - properties.put("illuminationDesc", "bright"); - properties.put("csq", 25); - - variables = new HashMap<>(); - variables.put("deviceId", "nex123321"); - variables.put("productKey", "Ru871cfJjhoM"); - variables.put("deviceName", "期望值验证"); - variables.put("iotId", "Ru871cfJjhoMnex123321"); - variables.put("deviceNode", "DEVICE"); - variables.put("messageType", "PROPERTIES"); - variables.put("properties", properties); - } - - /** - * 使用反射调用受保护的processTemplate方法 - */ - private String processTemplate(String template, Map variables) { - try { - java.lang.reflect.Method method = DefaultJdbcOutPlugin.class.getDeclaredMethod( - "processTemplate", String.class, Map.class); - method.setAccessible(true); - return (String) method.invoke(plugin, template, variables); - } catch (Exception e) { - throw new RuntimeException("调用processTemplate方法失败", e); - } - } - - @Test - @DisplayName("测试占位符在单引号内的情况(MySQL/PostgreSQL标准)") - void testPlaceholderInSingleQuotes() { - // 测试场景:'#{deviceId}' 应该替换为 'nex123321',不产生双重引号 - String template = "INSERT INTO device_data(device_id) VALUES('#{deviceId}');"; - String result = processTemplate(template, variables); - - System.out.println("模板: " + template); - System.out.println("结果: " + result); - - // 应该只包含一对单引号,不应该有双重引号 - assertEquals("INSERT INTO device_data(device_id) VALUES('nex123321');", result); - assertFalse(result.contains("''"), "不应该包含双重单引号"); - } - - @Test - @DisplayName("测试PostgreSQL json_build_object场景") - void testPostgreSQLJsonBuildObject() { - // 用户提供的实际场景 - String template = - "INSERT INTO device_data(device_id, product_key, device_name, iot_id, " + - "device_node, message_type, properties, raw_data) VALUES(" + - "'#{deviceId}', '#{productKey}', '#{deviceName}', '#{iotId}', " + - "'#{deviceNode}', '#{messageType}', '#{properties}', " + - "json_build_object(" + - "'csq', #{properties.temperature}, " + - "'tips', #{properties.illuminationDesc}" + - ")::TEXT);"; - - String result = processTemplate(template, variables); - - System.out.println("模板: " + template); - System.out.println("结果: " + result); - - // 验证单引号内的占位符被正确替换,不产生双重引号 - assertTrue(result.contains("'nex123321'"), "deviceId应该被正确替换"); - assertTrue(result.contains("'Ru871cfJjhoM'"), "productKey应该被正确替换"); - assertTrue(result.contains("'期望值验证'"), "deviceName应该被正确替换"); - - // 验证json_build_object中的占位符(不在引号内)被正确替换 - assertTrue(result.contains("14.3"), "temperature应该被替换为数字"); - assertTrue(result.contains("bright"), "illuminationDesc应该被替换"); - - // 不应该有双重引号 - assertFalse(result.contains("''nex123321''"), "不应该有双重单引号"); - } - - @Test - @DisplayName("测试占位符在双引号内的情况(PostgreSQL JSON)") - void testPlaceholderInDoubleQuotes() { - String template = "SELECT \"#{deviceId}\" as id;"; - String result = processTemplate(template, variables); - - System.out.println("模板: " + template); - System.out.println("结果: " + result); - - // 应该只包含一对双引号,不应该有双重引号 - assertEquals("SELECT \"nex123321\" as id;", result); - assertFalse(result.contains("\"\""), "不应该包含双重双引号"); - } - - @Test - @DisplayName("测试占位符不在引号内的情况") - void testPlaceholderNotInQuotes() { - String template = "SELECT #{deviceId} as id;"; - String result = processTemplate(template, variables); - - System.out.println("模板: " + template); - System.out.println("结果: " + result); - - // 不在引号内时,应该添加单引号 - assertEquals("SELECT 'nex123321' as id;", result); - } - - @Test - @DisplayName("测试字符串值中包含单引号的情况") - void testStringWithSingleQuote() { - variables.put("deviceName", "O'Brien"); - String template = "INSERT INTO device_data(device_name) VALUES('#{deviceName}');"; - String result = processTemplate(template, variables); - - System.out.println("模板: " + template); - System.out.println("结果: " + result); - - // 单引号应该被转义为两个单引号 - assertTrue(result.contains("O''Brien"), "单引号应该被转义"); - assertEquals("INSERT INTO device_data(device_name) VALUES('O''Brien');", result); - } - - @Test - @DisplayName("测试字符串值中包含双引号的情况") - void testStringWithDoubleQuote() { - variables.put("description", "He said \"Hello\""); - String template = "SELECT \"#{description}\" as desc;"; - String result = processTemplate(template, variables); - - System.out.println("模板: " + template); - System.out.println("结果: " + result); - - // 双引号应该被转义 - assertTrue(result.contains("\\\""), "双引号应该被转义"); - } - - @Test - @DisplayName("测试NULL值处理") - void testNullValue() { - variables.put("optionalField", null); - String template = "INSERT INTO device_data(optional_field) VALUES('#{optionalField}');"; - String result = processTemplate(template, variables); - - System.out.println("模板: " + template); - System.out.println("结果: " + result); - - // NULL值应该被替换为NULL关键字 - assertEquals("INSERT INTO device_data(optional_field) VALUES('NULL');", result); - } - - @Test - @DisplayName("测试数字值处理") - void testNumberValue() { - String template = "INSERT INTO device_data(temperature) VALUES(#{properties.temperature});"; - String result = processTemplate(template, variables); - - System.out.println("模板: " + template); - System.out.println("结果: " + result); - - // 数字值不在引号内时,应该直接使用,不添加引号 - assertTrue(result.contains("14.3"), "数字值应该被正确替换"); - assertFalse(result.contains("'14.3'"), "数字值不应该被引号包围"); - } - - @Test - @DisplayName("测试数字值在引号内的情况") - void testNumberInQuotes() { - String template = "INSERT INTO device_data(temperature) VALUES('#{properties.temperature}');"; - String result = processTemplate(template, variables); - - System.out.println("模板: " + template); - System.out.println("结果: " + result); - - // 数字值在引号内时,应该直接替换,不添加引号 - assertEquals("INSERT INTO device_data(temperature) VALUES('14.3');", result); - } - - @Test - @DisplayName("测试嵌套属性访问") - void testNestedPropertyAccess() { - String template = - "INSERT INTO device_data(csq, temp) VALUES(" + - "'#{properties.csq}', #{properties.temperature});"; - String result = processTemplate(template, variables); - - System.out.println("模板: " + template); - System.out.println("结果: " + result); - - assertTrue(result.contains("'25'"), "嵌套属性csq应该被正确替换"); - assertTrue(result.contains("14.3"), "嵌套属性temperature应该被正确替换"); - } - - @Test - @DisplayName("测试混合场景:部分在引号内,部分不在") - void testMixedQuotes() { - String template = - "INSERT INTO device_data(device_id, product_key, temperature) VALUES(" + - "'#{deviceId}', '#{productKey}', #{properties.temperature});"; - String result = processTemplate(template, variables); - - System.out.println("模板: " + template); - System.out.println("结果: " + result); - - // 在引号内的应该只替换值 - assertTrue(result.contains("'nex123321'"), "引号内的deviceId应该被正确替换"); - assertTrue(result.contains("'Ru871cfJjhoM'"), "引号内的productKey应该被正确替换"); - - // 不在引号内的应该添加引号 - assertTrue(result.contains("'14.3'"), "不在引号内的temperature应该被添加引号"); - } - - @Test - @DisplayName("测试复杂的PostgreSQL JSON场景") - void testComplexPostgreSQLJson() { - String template = - "INSERT INTO device_data(raw_data) VALUES(" + - "json_build_object(" + - "'deviceId', '#{deviceId}', " + - "'temperature', #{properties.temperature}, " + - "'description', '#{properties.illuminationDesc}'" + - ")::TEXT);"; - - String result = processTemplate(template, variables); - - System.out.println("模板: " + template); - System.out.println("结果: " + result); - - // 验证JSON字符串中的占位符被正确替换 - assertTrue(result.contains("'nex123321'"), "JSON中的deviceId应该被正确替换"); - assertTrue(result.contains("14.3"), "JSON中的temperature应该被正确替换"); - assertTrue(result.contains("'bright'"), "JSON中的description应该被正确替换"); - - // 不应该有双重引号 - assertFalse(result.contains("''nex123321''"), "不应该有双重单引号"); - } - - @Test - @DisplayName("测试布尔值处理") - void testBooleanValue() { - variables.put("isActive", true); - String template = "INSERT INTO device_data(is_active) VALUES(#{isActive});"; - String result = processTemplate(template, variables); - - System.out.println("模板: " + template); - System.out.println("结果: " + result); - - // 布尔值不在引号内时,应该直接使用 - assertTrue(result.contains("true"), "布尔值应该被正确替换"); - } - - @Test - @DisplayName("测试空字符串处理") - void testEmptyString() { - variables.put("emptyField", ""); - String template = "INSERT INTO device_data(empty_field) VALUES('#{emptyField}');"; - String result = processTemplate(template, variables); - - System.out.println("模板: " + template); - System.out.println("结果: " + result); - - // 空字符串应该被正确处理 - assertEquals("INSERT INTO device_data(empty_field) VALUES('');", result); - } - - @Test - @DisplayName("测试多个占位符在同一引号内") - void testMultiplePlaceholdersInQuotes() { - // 注意:这种情况在实际SQL中不常见,但测试边界情况 - String template = "SELECT '#{deviceId}_#{productKey}' as combined;"; - String result = processTemplate(template, variables); - - System.out.println("模板: " + template); - System.out.println("结果: " + result); - - // 两个占位符都在同一个引号内,应该都被正确替换 - assertTrue(result.contains("nex123321"), "第一个占位符应该被替换"); - assertTrue(result.contains("Ru871cfJjhoM"), "第二个占位符应该被替换"); - } -} - diff --git a/cn-universal-data-bridge/cn-universal-data-bridge-plugins/cn-universal-data-bridge-plugin-jdbc/src/test/java/cn/universal/databridge/plugin/jdbc/QuickTemplateTest.java b/cn-universal-data-bridge/cn-universal-data-bridge-plugins/cn-universal-data-bridge-plugin-jdbc/src/test/java/cn/universal/databridge/plugin/jdbc/QuickTemplateTest.java index dcf0a9c..6a91359 100644 --- a/cn-universal-data-bridge/cn-universal-data-bridge-plugins/cn-universal-data-bridge-plugin-jdbc/src/test/java/cn/universal/databridge/plugin/jdbc/QuickTemplateTest.java +++ b/cn-universal-data-bridge/cn-universal-data-bridge-plugins/cn-universal-data-bridge-plugin-jdbc/src/test/java/cn/universal/databridge/plugin/jdbc/QuickTemplateTest.java @@ -2,6 +2,9 @@ package cn.universal.databridge.plugin.jdbc; import static org.junit.jupiter.api.Assertions.*; +import cn.universal.databridge.engine.ParamSql; +import cn.universal.databridge.engine.ParamTemplateEngine; +import cn.universal.databridge.engine.dialect.MySqlDialectAdapter; import cn.universal.databridge.entity.DataBridgeConfig; import cn.universal.databridge.entity.PluginInfo; import cn.universal.databridge.entity.ResourceConnection; @@ -16,8 +19,7 @@ import org.junit.jupiter.api.Test; public class QuickTemplateTest { @Test - public void testGenericVsJdbcProcessing() { - // 创建测试数据 + public void testGenericTemplateProcessing() { Map properties = new HashMap<>(); properties.put("csq", "25"); properties.put("code", "200"); @@ -29,68 +31,70 @@ public class QuickTemplateTest { String template = "Device: #{deviceId}, CSQ: #{properties.csq}, Code: #{properties.code}"; - System.out.println("=== 通用 vs JDBC 模板处理对比 ==="); + System.out.println("=== 通用模板处理测试 ==="); System.out.println("原始模板: " + template); try { - // 测试通用实现 - AbstractDataBridgePlugin genericPlugin = new AbstractDataBridgePlugin() { - @Override - public PluginInfo getPluginInfo() { + AbstractDataBridgePlugin genericPlugin = + new AbstractDataBridgePlugin() { + @Override + public PluginInfo getPluginInfo() { return null; - } + } - @Override - public Boolean testConnection(ResourceConnection connection) { + @Override + public Boolean testConnection(ResourceConnection connection) { return null; - } + } - @Override - public Boolean validateConfig(DataBridgeConfig config) { + @Override + public Boolean validateConfig(DataBridgeConfig config) { return null; - } + } - @Override - public List getSupportedSourceScopes() { + @Override + public List getSupportedSourceScopes() { return List.of(); - } - }; - java.lang.reflect.Method genericMethod = - AbstractDataBridgePlugin.class.getDeclaredMethod("processTemplate", String.class, Map.class); + } + }; + java.lang.reflect.Method genericMethod = + AbstractDataBridgePlugin.class.getDeclaredMethod( + "processTemplate", String.class, Map.class); genericMethod.setAccessible(true); String genericResult = (String) genericMethod.invoke(genericPlugin, template, variables); System.out.println("通用处理结果: " + genericResult); - // 测试JDBC实现 - DefaultJdbcOutPlugin jdbcPlugin = new DefaultJdbcOutPlugin(); - java.lang.reflect.Method jdbcMethod = - DefaultJdbcOutPlugin.class.getDeclaredMethod("processTemplate", String.class, Map.class); - jdbcMethod.setAccessible(true); - String jdbcResult = (String) jdbcMethod.invoke(jdbcPlugin, template, variables); - - System.out.println("JDBC处理结果: " + jdbcResult); - - // 验证通用实现 - 直接字符串转换,无单引号 assertTrue(genericResult.contains("device001")); assertTrue(genericResult.contains("25")); assertTrue(genericResult.contains("200")); assertFalse(genericResult.contains("'device001'")); // 不应该有单引号 - // 验证JDBC实现 - SQL安全转换,有单引号 - assertTrue(jdbcResult.contains("'device001'")); // 应该有单引号 - assertTrue(jdbcResult.contains("'25'")); // 应该有单引号 - assertTrue(jdbcResult.contains("'200'")); // 应该有单引号 - - System.out.println("✅ 通用 vs JDBC 处理对比测试通过!"); - System.out.println("通用实现: 直接字符串转换,适合JSON等非SQL场景"); - System.out.println("JDBC实现: SQL安全转换,适合SQL场景"); + System.out.println("✅ 通用模板处理测试通过!"); } catch (Exception e) { fail("测试失败: " + e.getMessage()); } } + @Test + public void testJdbcParamTemplateDoesNotInlineSqlValues() { + Map properties = new HashMap<>(); + properties.put("remark", "x'); drop table device_data; --"); + + Map variables = new HashMap<>(); + variables.put("deviceId", "device001"); + variables.put("properties", properties); + + String template = + "insert into device_data(device_id, remark) values(#{deviceId}, #{properties.remark})"; + ParamSql result = new ParamTemplateEngine().process(template, variables, new MySqlDialectAdapter()); + + assertEquals("insert into device_data(device_id, remark) values(?, ?)", result.getSql()); + assertEquals(List.of("device001", "x'); drop table device_data; --"), result.getParams()); + assertFalse(result.getSql().contains("drop table")); + } + @Test public void testNestedPropertyAccess() { Map level2 = new HashMap<>(); @@ -104,31 +108,33 @@ public class QuickTemplateTest { String template = "Nested: #{root.level2.value}"; - AbstractDataBridgePlugin plugin = new AbstractDataBridgePlugin() { - @Override - public PluginInfo getPluginInfo() { + AbstractDataBridgePlugin plugin = + new AbstractDataBridgePlugin() { + @Override + public PluginInfo getPluginInfo() { return null; - } + } - @Override - public Boolean testConnection(ResourceConnection connection) { + @Override + public Boolean testConnection(ResourceConnection connection) { return null; - } + } - @Override - public Boolean validateConfig(DataBridgeConfig config) { + @Override + public Boolean validateConfig(DataBridgeConfig config) { return null; - } + } - @Override - public List getSupportedSourceScopes() { + @Override + public List getSupportedSourceScopes() { return List.of(); - } - }; + } + }; try { - java.lang.reflect.Method method = - AbstractDataBridgePlugin.class.getDeclaredMethod("processTemplate", String.class, Map.class); + java.lang.reflect.Method method = + AbstractDataBridgePlugin.class.getDeclaredMethod( + "processTemplate", String.class, Map.class); method.setAccessible(true); String result = (String) method.invoke(plugin, template, variables); diff --git a/cn-universal-data-bridge/cn-universal-data-bridge-plugins/cn-universal-data-bridge-plugin-jdbc/src/test/java/cn/universal/databridge/plugin/jdbc/SimpleTemplateTest.java b/cn-universal-data-bridge/cn-universal-data-bridge-plugins/cn-universal-data-bridge-plugin-jdbc/src/test/java/cn/universal/databridge/plugin/jdbc/SimpleTemplateTest.java deleted file mode 100644 index 947449c..0000000 --- a/cn-universal-data-bridge/cn-universal-data-bridge-plugins/cn-universal-data-bridge-plugin-jdbc/src/test/java/cn/universal/databridge/plugin/jdbc/SimpleTemplateTest.java +++ /dev/null @@ -1,64 +0,0 @@ -package cn.universal.databridge.plugin.jdbc; - -import static org.junit.jupiter.api.Assertions.*; - -import java.util.HashMap; -import java.util.Map; -import org.junit.jupiter.api.Test; - -/** 简单的模板语法测试 */ -public class SimpleTemplateTest { - - @Test - public void testSQLTemplate() { - // 创建测试数据 - Map properties = new HashMap<>(); - properties.put("csq", "25"); - properties.put("code", "200"); - properties.put("temperature", "23.5"); - - Map variables = new HashMap<>(); - variables.put("deviceId", "device001"); - variables.put("productKey", "product001"); - variables.put("deviceName", "测试设备"); - variables.put("iotId", "iot001"); - variables.put("deviceNode", "DEVICE"); - variables.put("messageType", "PROPERTIES"); - variables.put("properties", properties); - variables.put("data", "raw_data_content"); - - // 测试SQL模板 - String template = - "INSERT INTO device_data(device_id,product_key,device_name,iot_id,device_node,message_type,csq,code,temperature,properties,raw_data) " - + "VALUES(#{deviceId},#{productKey},#{deviceName},#{iotId},#{deviceNode},#{messageType},#{properties.csq},#{properties.code},#{properties.temperature},#{properties},#{data});"; - - DefaultJdbcOutPlugin plugin = new DefaultJdbcOutPlugin(); - - try { - java.lang.reflect.Method method = - DefaultJdbcOutPlugin.class.getDeclaredMethod("processTemplate", String.class, Map.class); - method.setAccessible(true); - String result = (String) method.invoke(plugin, template, variables); - - System.out.println("SQL模板测试:"); - System.out.println("原始模板: " + template); - System.out.println("处理结果: " + result); - - // 验证SQL结果 - assertTrue(result.contains("'device001'")); - assertTrue(result.contains("'product001'")); - assertTrue(result.contains("'测试设备'")); - assertTrue(result.contains("'iot001'")); - assertTrue(result.contains("'DEVICE'")); - assertTrue(result.contains("'PROPERTIES'")); - assertTrue(result.contains("'25'")); // properties.csq - assertTrue(result.contains("'200'")); // properties.code - assertTrue(result.contains("'23.5'")); // properties.temperature - - System.out.println("✅ SQL模板处理测试通过!"); - - } catch (Exception e) { - fail("测试失败: " + e.getMessage()); - } - } -} diff --git a/cn-universal-data-bridge/cn-universal-data-bridge-plugins/cn-universal-data-bridge-plugin-jdbc/src/test/java/cn/universal/databridge/plugin/jdbc/TemplateDemo.java b/cn-universal-data-bridge/cn-universal-data-bridge-plugins/cn-universal-data-bridge-plugin-jdbc/src/test/java/cn/universal/databridge/plugin/jdbc/TemplateDemo.java deleted file mode 100644 index e91544e..0000000 --- a/cn-universal-data-bridge/cn-universal-data-bridge-plugins/cn-universal-data-bridge-plugin-jdbc/src/test/java/cn/universal/databridge/plugin/jdbc/TemplateDemo.java +++ /dev/null @@ -1,116 +0,0 @@ -package cn.universal.databridge.plugin.jdbc; - -import cn.universal.databridge.entity.DataBridgeConfig; -import cn.universal.databridge.entity.PluginInfo; -import cn.universal.databridge.entity.ResourceConnection; -import cn.universal.databridge.plugin.AbstractDataBridgePlugin; -import cn.universal.databridge.plugin.SourceScope; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** 模板处理演示程序 */ -public class TemplateDemo { - - public static void main(String[] args) { - System.out.println("=== 模板处理功能演示 ===\n"); - - // 创建测试数据 - Map properties = new HashMap<>(); - properties.put("csq", "25"); - properties.put("code", "200"); - properties.put("temperature", "23.5"); - properties.put("location", Map.of("city", "Beijing", "country", "China")); - - Map variables = new HashMap<>(); - variables.put("deviceId", "device001"); - variables.put("productKey", "product001"); - variables.put("deviceName", "测试设备"); - variables.put("properties", properties); - - // 测试模板 - String template = - "Device: #{deviceId}, Product: #{productKey}, Name: #{deviceName}, " + - "CSQ: #{properties.csq}, Code: #{properties.code}, " + - "City: #{properties.location.city}, Country: #{properties.location.country}"; - - System.out.println("原始模板:"); - System.out.println(template); - System.out.println(); - - try { - // 1. 通用模板处理 - System.out.println("1. 通用模板处理 (AbstractDataBridgePlugin):"); - AbstractDataBridgePlugin genericPlugin = new AbstractDataBridgePlugin() { - @Override - public PluginInfo getPluginInfo() { - return null; - } - - @Override - public Boolean testConnection(ResourceConnection connection) { - return null; - } - - @Override - public Boolean validateConfig(DataBridgeConfig config) { - return null; - } - - @Override - public List getSupportedSourceScopes() { - return List.of(); - } - }; - java.lang.reflect.Method genericMethod = - AbstractDataBridgePlugin.class.getDeclaredMethod("processTemplate", String.class, Map.class); - genericMethod.setAccessible(true); - String genericResult = (String) genericMethod.invoke(genericPlugin, template, variables); - System.out.println("结果: " + genericResult); - System.out.println("特点: 直接字符串转换,无单引号,适合JSON等非SQL场景"); - System.out.println(); - - // 2. JDBC专用模板处理 - System.out.println("2. JDBC专用模板处理 (DefaultJdbcOutPlugin):"); - DefaultJdbcOutPlugin jdbcPlugin = new DefaultJdbcOutPlugin(); - java.lang.reflect.Method jdbcMethod = - DefaultJdbcOutPlugin.class.getDeclaredMethod("processTemplate", String.class, Map.class); - jdbcMethod.setAccessible(true); - String jdbcResult = (String) jdbcMethod.invoke(jdbcPlugin, template, variables); - System.out.println("结果: " + jdbcResult); - System.out.println("特点: SQL安全转换,有单引号,防止SQL注入"); - System.out.println(); - - // 3. 对比分析 - System.out.println("3. 对比分析:"); - System.out.println("通用实现 vs JDBC实现:"); - System.out.println("- 通用: device001 → device001 (无引号)"); - System.out.println("- JDBC: device001 → 'device001' (有引号)"); - System.out.println(); - System.out.println("- 通用: 25 → 25 (数字无引号)"); - System.out.println("- JDBC: 25 → '25' (数字有引号)"); - System.out.println(); - System.out.println("- 通用: Beijing → Beijing (字符串无引号)"); - System.out.println("- JDBC: Beijing → 'Beijing' (字符串有引号)"); - System.out.println(); - - // 4. 使用场景建议 - System.out.println("4. 使用场景建议:"); - System.out.println("通用实现适用于:"); - System.out.println("- JSON模板: {\"deviceId\": \"#{deviceId}\"}"); - System.out.println("- XML模板: "); - System.out.println("- 文本模板: Device #{deviceId} is online"); - System.out.println(); - System.out.println("JDBC实现适用于:"); - System.out.println("- SQL模板: INSERT INTO devices(id, name) VALUES(#{deviceId}, #{deviceName})"); - System.out.println("- 需要SQL注入防护的场景"); - System.out.println(); - - System.out.println("✅ 演示完成!"); - - } catch (Exception e) { - System.err.println("演示失败: " + e.getMessage()); - e.printStackTrace(); - } - } -} diff --git a/cn-universal-data-bridge/cn-universal-data-bridge-plugins/cn-universal-data-bridge-plugin-jdbc/src/test/java/cn/universal/databridge/plugin/jdbc/TemplateProcessingTest.java b/cn-universal-data-bridge/cn-universal-data-bridge-plugins/cn-universal-data-bridge-plugin-jdbc/src/test/java/cn/universal/databridge/plugin/jdbc/TemplateProcessingTest.java deleted file mode 100644 index f16fcb8..0000000 --- a/cn-universal-data-bridge/cn-universal-data-bridge-plugins/cn-universal-data-bridge-plugin-jdbc/src/test/java/cn/universal/databridge/plugin/jdbc/TemplateProcessingTest.java +++ /dev/null @@ -1,206 +0,0 @@ -package cn.universal.databridge.plugin.jdbc; - -import static org.junit.jupiter.api.Assertions.*; - -import cn.universal.common.constant.IoTConstant; -import java.util.HashMap; -import java.util.Map; -import org.junit.jupiter.api.Test; - -/** 模板处理测试类 测试嵌套属性访问功能 */ -public class TemplateProcessingTest { - @Test - public void testNestedPropertyJSON() { - // 创建测试数据 - Map properties = new HashMap<>(); - properties.put("csq", "25"); - properties.put("code", "200"); - properties.put("temperature", "23.5"); - - Map variables = new HashMap<>(); - variables.put("deviceId", "device001"); - variables.put("productKey", "product001"); - variables.put("deviceName", "测试设备"); - variables.put("iotId", "iot001"); - variables.put("deviceNode", IoTConstant.DeviceNode.DEVICE.toString()); - variables.put("messageType", "PROPERTIES"); - variables.put("properties", properties); - variables.put("data", "raw_data_content"); - - // 测试模板 - String template = - """ - { - "deviceId": "#{deviceId}", - "location": { - "city": "#{properties.code}", - "deviceNode": "#{deviceNode}", - "country": "#{properties.temperature}" - } - } - """; - - // 创建插件实例进行测试 - DefaultJdbcOutPlugin plugin = new DefaultJdbcOutPlugin(); - - // 使用反射调用受保护方法进行测试 - try { - java.lang.reflect.Method method = - DefaultJdbcOutPlugin.class.getDeclaredMethod("processTemplate", String.class, Map.class); - method.setAccessible(true); - String result = (String) method.invoke(plugin, template, variables); - - System.out.println("原始模板:"); - System.out.println(template); - System.out.println("\n处理结果:"); - System.out.println(result); - - // 验证结果 - assertTrue(result.contains("'device001'")); - assertTrue(result.contains("'product001'")); - assertTrue(result.contains("'测试设备'")); - assertTrue(result.contains("'iot001'")); - assertTrue(result.contains("'DEVICE'")); - assertTrue(result.contains("'PROPERTIES'")); - assertTrue(result.contains("'25'")); // properties.csq - assertTrue(result.contains("'200'")); // properties.code - assertTrue(result.contains("'23.5'")); // properties.temperature - - System.out.println("\n✅ 嵌套属性访问测试通过!"); - - } catch (Exception e) { - fail("测试失败: " + e.getMessage()); - } - } - - @Test - public void testNestedPropertyAccess() { - // 创建测试数据 - Map properties = new HashMap<>(); - properties.put("csq", "25"); - properties.put("code", "200"); - properties.put("temperature", "23.5"); - - Map variables = new HashMap<>(); - variables.put("deviceId", "device001"); - variables.put("productKey", "product001"); - variables.put("deviceName", "测试设备"); - variables.put("iotId", "iot001"); - variables.put("deviceNode", "DEVICE"); - variables.put("messageType", "PROPERTIES"); - variables.put("properties", properties); - variables.put("data", "raw_data_content"); - - // 测试模板 - String template = - "INSERT INTO device_data(device_id,product_key,device_name,iot_id,device_node,message_type,csq,code,temperature,properties,raw_data) " - + "VALUES(#{deviceId},#{productKey},#{deviceName},#{iotId},#{deviceNode},#{messageType},#{properties.csq},#{properties.code},#{properties.temperature},#{properties},#{data});"; - - // 创建插件实例进行测试 - DefaultJdbcOutPlugin plugin = new DefaultJdbcOutPlugin(); - - // 使用反射调用受保护方法进行测试 - try { - java.lang.reflect.Method method = - DefaultJdbcOutPlugin.class.getDeclaredMethod("processTemplate", String.class, Map.class); - method.setAccessible(true); - String result = (String) method.invoke(plugin, template, variables); - - System.out.println("原始模板:"); - System.out.println(template); - System.out.println("\n处理结果:"); - System.out.println(result); - - // 验证结果 - assertTrue(result.contains("'device001'")); - assertTrue(result.contains("'product001'")); - assertTrue(result.contains("'测试设备'")); - assertTrue(result.contains("'iot001'")); - assertTrue(result.contains("'DEVICE'")); - assertTrue(result.contains("'PROPERTIES'")); - assertTrue(result.contains("'25'")); // properties.csq - assertTrue(result.contains("'200'")); // properties.code - assertTrue(result.contains("'23.5'")); // properties.temperature - - System.out.println("\n✅ 嵌套属性访问测试通过!"); - - } catch (Exception e) { - fail("测试失败: " + e.getMessage()); - } - } - - @Test - public void testNestedPropertyWithNull() { - // 测试嵌套属性为null的情况 - Map properties = new HashMap<>(); - properties.put("csq", "25"); - properties.put("code", null); // 故意设置为null - properties.put("temperature", "23.5"); - - Map variables = new HashMap<>(); - variables.put("deviceId", "device001"); - variables.put("properties", properties); - - String template = - "INSERT INTO test_table(csq,code,temperature) VALUES(#{properties.csq},#{properties.code},#{properties.temperature});"; - - DefaultJdbcOutPlugin plugin = new DefaultJdbcOutPlugin(); - - try { - java.lang.reflect.Method method = - DefaultJdbcOutPlugin.class.getDeclaredMethod("processTemplate", String.class, Map.class); - method.setAccessible(true); - String result = (String) method.invoke(plugin, template, variables); - - System.out.println("测试null值处理:"); - System.out.println("模板: " + template); - System.out.println("结果: " + result); - - // 验证null值被转换为SQL的NULL - assertTrue(result.contains("'25'")); - assertTrue(result.contains("NULL")); // properties.code为null - assertTrue(result.contains("'23.5'")); - - System.out.println("✅ null值处理测试通过!"); - - } catch (Exception e) { - fail("测试失败: " + e.getMessage()); - } - } - - @Test - public void testNonExistentNestedProperty() { - // 测试不存在的嵌套属性 - Map properties = new HashMap<>(); - properties.put("csq", "25"); - - Map variables = new HashMap<>(); - variables.put("deviceId", "device001"); - variables.put("properties", properties); - - String template = - "INSERT INTO test_table(csq,non_existent) VALUES(#{properties.csq},#{properties.nonExistent});"; - - DefaultJdbcOutPlugin plugin = new DefaultJdbcOutPlugin(); - - try { - java.lang.reflect.Method method = - DefaultJdbcOutPlugin.class.getDeclaredMethod("processTemplate", String.class, Map.class); - method.setAccessible(true); - String result = (String) method.invoke(plugin, template, variables); - - System.out.println("测试不存在的嵌套属性:"); - System.out.println("模板: " + template); - System.out.println("结果: " + result); - - // 验证不存在的属性被转换为SQL的NULL - assertTrue(result.contains("'25'")); - assertTrue(result.contains("NULL")); // 不存在的属性 - - System.out.println("✅ 不存在属性处理测试通过!"); - - } catch (Exception e) { - fail("测试失败: " + e.getMessage()); - } - } -} diff --git a/cn-universal-framework/cn-universal-dm/src/main/java/cn/universal/dm/device/service/log/CkDeviceLogService.java b/cn-universal-framework/cn-universal-dm/src/main/java/cn/universal/dm/device/service/log/CkDeviceLogService.java index f94ee22..a286f54 100644 --- a/cn-universal-framework/cn-universal-dm/src/main/java/cn/universal/dm/device/service/log/CkDeviceLogService.java +++ b/cn-universal-framework/cn-universal-dm/src/main/java/cn/universal/dm/device/service/log/CkDeviceLogService.java @@ -410,8 +410,8 @@ public class CkDeviceLogService extends AbstractIoTDeviceLogService { } if (MapUtil.isNotEmpty(logQuery.getParams()) && ObjectUtil.isNotNull(logQuery.getParams().get("properties"))) { - sql.append( - " AND JSONHas(content,'properties','" + logQuery.getParams().get("properties") + "')=1"); + sql.append(" AND JSONHas(content,'properties',?)=1"); + params.add(logQuery.getParams().get("properties")); } if (MapUtil.isNotEmpty(logQuery.getParams()) && ObjectUtil.isNotNull(logQuery.getParams().get("event"))) { diff --git a/cn-universal-persistence/src/main/java/cn/universal/persistence/mapper/admin/SysMenuMapper.java b/cn-universal-persistence/src/main/java/cn/universal/persistence/mapper/admin/SysMenuMapper.java index e2aa3df..8ddf051 100644 --- a/cn-universal-persistence/src/main/java/cn/universal/persistence/mapper/admin/SysMenuMapper.java +++ b/cn-universal-persistence/src/main/java/cn/universal/persistence/mapper/admin/SysMenuMapper.java @@ -72,11 +72,4 @@ public interface SysMenuMapper extends BaseMapper { public List selectMenuListByRoleId( @Param("roleId") Long roleId, @Param("menuCheckStrictly") boolean menuCheckStrictly); - /** - * 执行菜单脚本 - * - * @param sql 菜单脚本 - * @return 返回null - */ - Integer execMenuSql(@Param("sql") String sql); } diff --git a/cn-universal-persistence/src/main/resources/mybatis/IoTDeviceMapper.xml b/cn-universal-persistence/src/main/resources/mybatis/IoTDeviceMapper.xml index f35bb35..486cde5 100644 --- a/cn-universal-persistence/src/main/resources/mybatis/IoTDeviceMapper.xml +++ b/cn-universal-persistence/src/main/resources/mybatis/IoTDeviceMapper.xml @@ -305,7 +305,7 @@ AND p.product_key = #{productKey} - AND p.company_no like '%${companyNo}%' + AND p.company_no like concat('%', #{companyNo}, '%') AND a.creator_id = #{iotUnionId} @@ -347,7 +347,7 @@ AND p.product_id = #{productId} - AND p.company_no like '%${companyNo}%' + AND p.company_no like concat('%', #{companyNo}, '%') AND a.creator_id = #{iotUnionId} @@ -475,9 +475,6 @@ and d.creator_id=#{unionId} - - - ORDER BY d.create_time DESC @@ -556,9 +553,6 @@ and d.creator_id=#{unionId} - - - ORDER BY d.create_time DESC @@ -832,9 +826,6 @@ and registry_time = #{dev.registryTime} and online_time = #{dev.onlineTime} and areas_id = #{dev.areasId} - - AND ( ${dev.params.scope} ) - and creator_id=#{unionId} diff --git a/cn-universal-persistence/src/main/resources/mybatis/IoTProductMapper.xml b/cn-universal-persistence/src/main/resources/mybatis/IoTProductMapper.xml index 21b629a..d9dbbd0 100644 --- a/cn-universal-persistence/src/main/resources/mybatis/IoTProductMapper.xml +++ b/cn-universal-persistence/src/main/resources/mybatis/IoTProductMapper.xml @@ -50,7 +50,7 @@ AND device_node = #{deviceNode} - AND `name` like '%${productName}%' + AND `name` like concat('%', #{productName}, '%') AND a.transport_protocol = #{transportProtocol} @@ -59,7 +59,7 @@ AND product_id = #{productId} - AND company_no like '%${companyNo}%' + AND company_no like concat('%', #{companyNo}, '%') @@ -670,4 +670,4 @@ WHERE product_key = #{productKey} AND is_deleted = 0 - \ No newline at end of file + diff --git a/cn-universal-persistence/src/main/resources/mybatis/IoTUserMapper.xml b/cn-universal-persistence/src/main/resources/mybatis/IoTUserMapper.xml index 015de06..4482c29 100644 --- a/cn-universal-persistence/src/main/resources/mybatis/IoTUserMapper.xml +++ b/cn-universal-persistence/src/main/resources/mybatis/IoTUserMapper.xml @@ -69,9 +69,6 @@ and date_format(create_date,'%y%m%d') <= date_format(#{params.endTime},'%y%m%d') - - - @@ -126,4 +123,4 @@ or (login_date is not null and DATEDIFF(NOW(), login_date) >= 90)) - \ No newline at end of file + diff --git a/cn-universal-persistence/src/main/resources/mybatis/SysLogininforMapper.xml b/cn-universal-persistence/src/main/resources/mybatis/SysLogininforMapper.xml index 317d98a..99ff677 100644 --- a/cn-universal-persistence/src/main/resources/mybatis/SysLogininforMapper.xml +++ b/cn-universal-persistence/src/main/resources/mybatis/SysLogininforMapper.xml @@ -47,10 +47,7 @@ and date_format(login_time,'%y%m%d') <= date_format(#{params.endTime},'%y%m%d') - - - order by login_time desc - \ No newline at end of file + diff --git a/cn-universal-persistence/src/main/resources/mybatis/SysOperLogMapper.xml b/cn-universal-persistence/src/main/resources/mybatis/SysOperLogMapper.xml index b6c8704..be46d73 100644 --- a/cn-universal-persistence/src/main/resources/mybatis/SysOperLogMapper.xml +++ b/cn-universal-persistence/src/main/resources/mybatis/SysOperLogMapper.xml @@ -45,10 +45,7 @@ and date_format(oper_time,'%y%m%d') <= date_format(#{params.endTime},'%y%m%d') - - - order by oper_time desc - \ No newline at end of file + diff --git a/cn-universal-persistence/src/main/resources/mybatis/admin/SysMenuMapper.xml b/cn-universal-persistence/src/main/resources/mybatis/admin/SysMenuMapper.xml index e9e1bc4..5ccb423 100644 --- a/cn-universal-persistence/src/main/resources/mybatis/admin/SysMenuMapper.xml +++ b/cn-universal-persistence/src/main/resources/mybatis/admin/SysMenuMapper.xml @@ -41,10 +41,6 @@ create_time from sys_menu - - - \ No newline at end of file + diff --git a/cn-universal-persistence/src/main/resources/mybatis/admin/SysRoleMapper.xml b/cn-universal-persistence/src/main/resources/mybatis/admin/SysRoleMapper.xml index 25d8b0c..6c0cd77 100644 --- a/cn-universal-persistence/src/main/resources/mybatis/admin/SysRoleMapper.xml +++ b/cn-universal-persistence/src/main/resources/mybatis/admin/SysRoleMapper.xml @@ -80,9 +80,6 @@ and date_format(r.create_time,'%y%m%d') <= date_format(#{params.endTime},'%y%m%d') - - - and create_by=#{createBy}