新增:利元亨线程,增加mocksql接口,用于测试内网生产sql性能

This commit is contained in:
2026-01-28 14:53:37 +08:00
parent ec04f4d6a2
commit fad77a674d
4 changed files with 144 additions and 3 deletions

View File

@@ -0,0 +1,12 @@
package com.sdm.outbridge.dao;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
import java.util.Map;
public interface DynamicSqlExecutorMapper {
@Select("${sql}") // 核心:用${sql}直接拼接传入的SQL字符串
List<Map<String, Object>> executeCustomSql(@Param("sql") String sql);
}

View File

@@ -6,6 +6,7 @@ import com.sdm.outbridge.mode.FreelinkAndDingdingInformReq;
import com.sdm.outbridge.mode.GetProcessDataReq;
import com.sdm.outbridge.mode.HkUploadFileReq;
import com.sdm.outbridge.service.lyric.*;
import com.sdm.pbs.service.lyricDbMock.DynamicSqlExecutor;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
@@ -15,6 +16,7 @@ import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
@Slf4j
@RestController
@@ -38,6 +40,9 @@ public class TestSecondDbController {
@Autowired
private LyricVTodoInfoService lyricVTodoInfoService;
@Autowired
private DynamicSqlExecutor dynamicSqlExecutor;
@@ -154,6 +159,92 @@ public class TestSecondDbController {
/**
* 执行查询SQL接口
* @return SQL执行结果
*/
@PostMapping("/mockSql")
public SdmResponse<List<Map<String, Object>>> executeSql(@RequestBody Map paramMap) {
try {
String sql = paramMap.get("sql").toString().trim();
// 安全校验禁止执行危险SQL生产环境必须加
validateSql(sql);
List<Map<String, Object>> result = dynamicSqlExecutor.executeQuery(sql);
return SdmResponse.success( result);
} catch (Exception e) {
return SdmResponse.failed("执行失败: " + e.getMessage());
}
}
/**
* SQL安全校验生产环境必须实现
* 1. 禁止DROP/ALTER/TRUNCATE等危险操作
* 2. 仅允许SELECT语句
* 3. SELECT语句必须以LIMIT结尾支持空格、换行、注释等边界情况
*/
private void validateSql(String sql) {
// 1. 空值校验
if (sql == null || sql.trim().isEmpty()) {
throw new RuntimeException("SQL语句不能为空");
}
// 2. SQL预处理去除注释、多余空格/换行,统一格式
String processedSql = preprocessSql(sql);
String lowerSql = processedSql.toLowerCase();
// 3. 禁止的危险关键字(精准匹配独立关键字,避免误判)
String[] forbiddenKeywords = {"drop", "alter", "truncate", "delete", "update", "insert", "create",
"rename", "replace", "grant", "revoke", "call", "execute"};
for (String keyword : forbiddenKeywords) {
if (containsIndependentKeyword(lowerSql, keyword)) {
throw new RuntimeException("禁止执行包含[" + keyword + "]的SQL语句");
}
}
// 4. 确保是SELECT语句
if (!lowerSql.startsWith("select")) {
throw new RuntimeException("仅允许执行SELECT查询语句");
}
// 5. 新增SELECT语句必须以LIMIT结尾支持LIMIT 数字 / LIMIT 数字,数字 / LIMIT 数字 OFFSET 数字)
if (!isEndWithValidLimit(lowerSql)) {
throw new RuntimeException("SELECT语句必须以LIMIT子句结尾格式支持LIMIT [数字] 或 LIMIT [数字],[数字] 或 LIMIT [数字] OFFSET [数字]");
}
}
/**
* 私有方法SQL预处理去除注释、多余空格/换行)
*/
private String preprocessSql(String sql) {
// 去除多行注释 /* ... */
String noMultiComment = sql.replaceAll("/\\*.*?\\*/", "");
// 去除单行注释 -- ...正确使用Pattern.MULTILINE避免参数错误
Pattern singleCommentPattern = Pattern.compile("--.*?$", Pattern.MULTILINE);
String noSingleComment = singleCommentPattern.matcher(noMultiComment).replaceAll("");
// 去除所有多余空白符(空格、换行、制表符等),压缩为单个空格
String noExtraSpace = noSingleComment.replaceAll("\\s+", " ").trim();
return noExtraSpace;
}
/**
* 私有方法检查是否包含独立的关键字避免误判如update_time不会匹配update
*/
private boolean containsIndependentKeyword(String sql, String keyword) {
Pattern pattern = Pattern.compile("(^|\\W)" + keyword + "(\\W|$)");
return pattern.matcher(sql).find();
}
/**
* 私有方法检查SQL是否以合法的LIMIT子句结尾
*/
private boolean isEndWithValidLimit(String lowerSql) {
// 正则匹配规则:支持 LIMIT 数字 / LIMIT 数字,数字 / LIMIT 数字 OFFSET 数字
Pattern limitPattern = Pattern.compile("^.*limit\\s+\\d+(\\s*,\\s*\\d+)?(\\s+offset\\s+\\d+)?$");
return limitPattern.matcher(lowerSql).matches();
}
}

View File

@@ -0,0 +1,41 @@
package com.sdm.pbs.service.lyricDbMock;
import com.sdm.outbridge.dao.DynamicSqlExecutorMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
/**
* 动态SQL执行工具类适配多数据源
*/
@Component
@Slf4j
public class DynamicSqlExecutor {
// 注入纯注解版Mapper接口多数据源需加@Qualifier指定
@Autowired
private DynamicSqlExecutorMapper dynamicSqlExecutorMapper;
/**
* 执行查询SQL返回List<Map>(通用格式)
* 关键修复2直接使用SqlSessionTemplate的selectList方法无需获取原生SqlSession
*/
public List<Map<String, Object>> executeQuery(String sql) {
try {
long start = System.currentTimeMillis();
// MyBatis执行动态SQL的通用方式使用STATEMENT类型执行原生SQL
List<Map<String, Object>> maps = dynamicSqlExecutorMapper.executeCustomSql(sql);
long end = System.currentTimeMillis();
log.info("mockSql:{},本次查询返回:{}条,耗时:{} s",sql,maps.size(),(end - start)/1000);
return maps;
} catch (Exception e) {
log.error("执行动态SQL失败{}", e.getMessage());
e.printStackTrace();
throw new RuntimeException("执行SQL出错: " + e.getMessage(), e);
}
}
}

View File

@@ -22,7 +22,4 @@ public class FormConfigure{
public String userId;
// 图表是1 动态表格2
public String type;
}