-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Description
Contact Information
MaxKB Version
v1.10.3-lts
Problem Description
@tool(name = "generate_and_download_excel_tool", description = "excel工具:生成excel并返回下载地址")
@OverRide
public String generateAndDownloadExcel(@ToolParam(description = "oracle查询语句,查询列使用字段注释做为别名,可以直接使用预览数据的sql") String querySql) {
log.info("生成excel文件--->{}", querySql);
querySql = querySql.trim();
if (querySql.endsWith(";")) {
querySql = querySql.substring(0, querySql.length() - 1);
}
if (!isQuerySql(querySql)) {
throw new QzException("请输入正确的sql语句");
}
//查询数据条数
long totalCount = getTotalCount(querySql);
log.info("查询数据总条数--->{}", totalCount);
List<String> columns = extractColumns(querySql);
log.info("查询字段--->{}", columns);
//生成excel字段列的表头
List<List<String>> head = generateHead(columns);
//查询完整数据
querySql = String.format("SELECT * FROM ( %s ) WHERE ROWNUM <= %s", querySql, Math.min(totalCount, excelConfig.getDownloadCount()));
log.info("查询数据内容--->{}", querySql);
List<Map<String, Object>> datas = jdbcTemplate.queryForList(querySql);
log.info("下载数据条数--->{}", datas.size());
//这里需要转换一下,因为 easyExcel 不支持 Map<String,Object> , 按照表头的顺序,生成对应的行数据
List<List<Object>> rows = datas.stream().map(m -> columns.stream().map(m::get).toList()).toList();
//生成临时文件名和文件路径
String fileName = String.format("output-%s.xlsx", IdUtil.getSnowflakeNextIdStr());
String filePath = System.getProperty("java.io.tmpdir") + fileName;
// 写入 Excel
EasyExcel.write(filePath).sheet("Sheet1")
//设置自动合并表头
.automaticMergeHead(true)
// 设置列宽自适应(不太精准)
// .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
// 设置表头
.head(head)
// 写入数据
.doWrite(rows);
//生成文件下载地址
String downloadUrl = excelConfig.getDownloadPath() + fileName;
log.info("下载文件地址--->{}", downloadUrl);
return downloadUrl;
}
public List extractColumns(String sql) {
List columns = new ArrayList<>();
// 1. 给 SQL 加限制,只查询表结构,不真正查询数据
String limitedSql = String.format("SELECT * FROM ( %s ) WHERE 1=0", sql);
log.info("查询字段的SQL--->{}", limitedSql);
// 2. 执行 SQL 获取字段元数据
try (Connection conn = dataSource.getConnection(); PreparedStatement ps = conn.prepareStatement(limitedSql); ResultSet rs = ps.executeQuery()) {
ResultSetMetaData meta = rs.getMetaData();
int count = meta.getColumnCount();
for (int i = 1; i <= count; i++) {
//字段别名 getColumnLabel 原始字段名是 getColumnName
columns.add(meta.getColumnLabel(i));
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
return columns;
}
Steps to Reproduce
1、启动这个工具服务
2、maxkb中配置调用工具
{
"McpServer": {
"url": "http://172.16.55.251:7390/sse",
"transport":"sse"
}
}
3、调试运行,帮我导出xxx数据
The expected correct result
java.lang.RuntimeException: java.sql.SQLException: interrupt
at com.qzdatasoft.mcpserver.tools.impl.ExcelToolsImpl.extractColumns(ExcelToolsImpl.java:196) ~[classes/:na]
at com.qzdatasoft.mcpserver.tools.impl.ExcelToolsImpl.generateAndDownloadExcel(ExcelToolsImpl.java:72) ~[classes/:na]
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:580) ~[na:na]
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:359) ~[spring-aop-6.2.8.jar:6.2.8]
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:196) ~[spring-aop-6.2.8.jar:6.2.8]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-6.2.8.jar:6.2.8]
at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:89) ~[spring-aop-6.2.8.jar:6.2.8]
at com.qzdatasoft.mcpserver.config.McpExceptionHandlerAspect.handleException(McpExceptionHandlerAspect.java:24) ~[classes/:na]
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:580) ~[na:na]
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:642) ~[spring-aop-6.2.8.jar:6.2.8]
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:632) ~[spring-aop-6.2.8.jar:6.2.8]
at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:71) ~[spring-aop-6.2.8.jar:6.2.8]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:173) ~[spring-aop-6.2.8.jar:6.2.8]
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97) ~[spring-aop-6.2.8.jar:6.2.8]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.2.8.jar:6.2.8]
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:728) ~[spring-aop-6.2.8.jar:6.2.8]
at com.qzdatasoft.mcpserver.tools.impl.ExcelToolsImpl$$SpringCGLIB$$0.generateAndDownloadExcel() ~[classes/:na]
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:580) ~[na:na]
at org.springframework.ai.tool.method.MethodToolCallback.callMethod(MethodToolCallback.java:166) ~[spring-ai-model-1.1.0-M2.jar:1.1.0-M2]
at org.springframework.ai.tool.method.MethodToolCallback.call(MethodToolCallback.java:109) ~[spring-ai-model-1.1.0-M2.jar:1.1.0-M2]
at org.springframework.ai.mcp.McpToolUtils.lambda$toSharedSyncToolSpecification$5(McpToolUtils.java:241) ~[spring-ai-mcp-1.1.0-M2.jar:1.1.0-M2]
at org.springframework.ai.mcp.McpToolUtils.lambda$toSyncToolSpecification$2(McpToolUtils.java:204) ~[spring-ai-mcp-1.1.0-M2.jar:1.1.0-M2]
at org.springframework.ai.mcp.McpToolUtils.lambda$toAsyncToolSpecification$6(McpToolUtils.java:358) ~[spring-ai-mcp-1.1.0-M2.jar:1.1.0-M2]
at reactor.core.publisher.MonoCallable.call(MonoCallable.java:72) ~[reactor-core-3.7.7.jar:3.7.7]
at reactor.core.publisher.FluxSubscribeOnCallable$CallableSubscribeOnSubscription.run(FluxSubscribeOnCallable.java:228) ~[reactor-core-3.7.7.jar:3.7.7]
at reactor.core.scheduler.SchedulerTask.call(SchedulerTask.java:68) ~[reactor-core-3.7.7.jar:3.7.7]
at reactor.core.scheduler.SchedulerTask.call(SchedulerTask.java:28) ~[reactor-core-3.7.7.jar:3.7.7]
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317) ~[na:na]
at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304) ~[na:na]
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144) ~[na:na]
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) ~[na:na]
at java.base/java.lang.Thread.run(Thread.java:1583) ~[na:na]
Caused by: java.sql.SQLException: interrupt
at com.alibaba.druid.pool.DruidDataSource.getConnectionInternal(DruidDataSource.java:1739) ~[druid-1.2.23.jar:na]
at com.alibaba.druid.pool.DruidDataSource.getConnectionDirect(DruidDataSource.java:1502) ~[druid-1.2.23.jar:na]
at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:1482) ~[druid-1.2.23.jar:na]
at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:1463) ~[druid-1.2.23.jar:na]
at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:83) ~[druid-1.2.23.jar:na]
at com.qzdatasoft.mcpserver.tools.impl.ExcelToolsImpl.extractColumns(ExcelToolsImpl.java:188) ~[classes/:na]
... 34 common frames omitted
Caused by: java.lang.InterruptedException: null
at java.base/java.util.concurrent.locks.ReentrantLock$Sync.lockInterruptibly(ReentrantLock.java:159) ~[na:na]
at java.base/java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:372) ~[na:na]
at com.alibaba.druid.pool.DruidDataSource.getConnectionInternal(DruidDataSource.java:1736) ~[druid-1.2.23.jar:na]
... 39 common frames omitted
Related log output
Additional Information
No response