diff --git a/ruoyi-oa/src/main/java/com/ruoyi/oa/domain/vo/DynamicDataVo.java b/ruoyi-oa/src/main/java/com/ruoyi/oa/domain/vo/DynamicDataVo.java index 2375fa5..a7ffaa0 100644 --- a/ruoyi-oa/src/main/java/com/ruoyi/oa/domain/vo/DynamicDataVo.java +++ b/ruoyi-oa/src/main/java/com/ruoyi/oa/domain/vo/DynamicDataVo.java @@ -20,9 +20,14 @@ public class DynamicDataVo { private Meta meta; /** - * 实际数据 + * 响应数据(根据renderType不同而不同) */ - private List> data; + private Object response; + + /** + * 渲染类型:text | table | chart | fix + */ + private String renderType; /** * 元数据类 @@ -60,4 +65,36 @@ public class DynamicDataVo { */ private String format; } + + /** + * 表格数据格式 + */ + @Data + public static class TableData { + /** + * 列信息 + */ + private List> columns; + + /** + * 数据源 + */ + private List> datasource; + } + + /** + * 图表数据格式 + */ + @Data + public static class ChartData { + /** + * 图表配置选项 + */ + private Object options; + + /** + * 数据集 + */ + private List> dataset; + } } \ No newline at end of file diff --git a/ruoyi-oa/src/main/java/com/ruoyi/oa/service/impl/AiDataQueryServiceImpl.java b/ruoyi-oa/src/main/java/com/ruoyi/oa/service/impl/AiDataQueryServiceImpl.java index 5363490..d4f81f7 100644 --- a/ruoyi-oa/src/main/java/com/ruoyi/oa/service/impl/AiDataQueryServiceImpl.java +++ b/ruoyi-oa/src/main/java/com/ruoyi/oa/service/impl/AiDataQueryServiceImpl.java @@ -67,10 +67,16 @@ public class AiDataQueryServiceImpl implements IAiDataQueryService { DynamicDataVo result = databaseQueryService.executeQueryWithMeta(sql, primaryTable, queryBo.getIncludeMeta()); // 9. 限制返回记录数 - if (result.getData() != null && result.getData().size() > queryBo.getLimit()) { - result.setData(result.getData().subList(0, queryBo.getLimit())); + if (result.getResponse() instanceof DynamicDataVo.TableData) { + DynamicDataVo.TableData tableData = (DynamicDataVo.TableData) result.getResponse(); + if (tableData.getDatasource() != null && tableData.getDatasource().size() > queryBo.getLimit()) { + tableData.setDatasource(tableData.getDatasource().subList(0, queryBo.getLimit())); + } } + // 10. 根据数据内容智能选择renderType + result = determineRenderType(result, queryBo.getQuery()); + return result; } catch (Exception e) { @@ -98,7 +104,9 @@ public class AiDataQueryServiceImpl implements IAiDataQueryService { prompt.append("2. 生成的SQL必须语法正确,可以直接执行\n"); prompt.append("3. 如果涉及多表查询,请使用适当的JOIN语句\n"); prompt.append("4. 请根据用户需求添加适当的WHERE条件、ORDER BY、LIMIT等子句\n"); - prompt.append("5. 只返回SQL语句,不要包含其他解释文字\n\n"); + prompt.append("5. 确保查询能返回有意义的数据,避免空结果\n"); + prompt.append("6. 如果查询可能返回空结果,请添加适当的默认条件或使用COALESCE等函数\n"); + prompt.append("7. 只返回SQL语句,不要包含其他解释文字\n\n"); // 用户需求 prompt.append("用户需求:").append(userQuery).append("\n\n"); @@ -130,7 +138,12 @@ public class AiDataQueryServiceImpl implements IAiDataQueryService { } // 输出要求 - prompt.append("请根据以上信息生成SQL查询语句,只返回SQL语句本身,不要包含任何其他文字。"); + prompt.append("请根据以上信息生成SQL查询语句,确保查询能返回有意义的数据。如果查询可能返回空结果,请考虑:\n"); + prompt.append("1. 使用LEFT JOIN而不是INNER JOIN\n"); + prompt.append("2. 添加适当的默认值或使用COALESCE函数\n"); + prompt.append("3. 使用LIMIT限制返回记录数(建议50条以内)\n"); + prompt.append("4. 添加ORDER BY确保结果有序\n"); + prompt.append("只返回SQL语句本身,不要包含任何其他文字。"); return prompt.toString(); } @@ -347,4 +360,146 @@ public class AiDataQueryServiceImpl implements IAiDataQueryService { log.info("SQL验证通过: {}", cleanedResponse); return cleanedResponse; } + + /** + * 根据数据内容和用户查询智能选择renderType并设置对应的数据格式 + */ + private DynamicDataVo determineRenderType(DynamicDataVo result, String userQuery) { + // 默认使用table类型 + String renderType = "table"; + + // 如果用户查询包含特定关键词,选择对应的renderType + String lowerQuery = userQuery.toLowerCase(); + + if (lowerQuery.contains("统计") || lowerQuery.contains("图表") || lowerQuery.contains("趋势") || + lowerQuery.contains("分析") || lowerQuery.contains("可视化")) { + renderType = "chart"; + } else if (lowerQuery.contains("文本") || lowerQuery.contains("描述") || lowerQuery.contains("说明")) { + renderType = "text"; + } else if (lowerQuery.contains("复合") || lowerQuery.contains("多个") || lowerQuery.contains("组合")) { + renderType = "fix"; + } + + // 根据renderType设置对应的数据格式 + if (result.getResponse() instanceof DynamicDataVo.TableData) { + DynamicDataVo.TableData tableData = (DynamicDataVo.TableData) result.getResponse(); + + switch (renderType) { + case "text": + // 文本格式:返回字符串 + if (tableData.getDatasource() == null || tableData.getDatasource().isEmpty()) { + result.setResponse("暂无相关数据"); + } else { + // 将表格数据转换为文本描述 + StringBuilder textResponse = new StringBuilder(); + textResponse.append("查询结果:\n"); + textResponse.append("共找到 ").append(tableData.getDatasource().size()).append(" 条记录\n\n"); + + for (int i = 0; i < Math.min(tableData.getDatasource().size(), 5); i++) { + Map row = tableData.getDatasource().get(i); + textResponse.append("记录 ").append(i + 1).append(":"); + for (Map.Entry entry : row.entrySet()) { + textResponse.append(entry.getKey()).append("=").append(entry.getValue()).append(", "); + } + textResponse.append("\n"); + } + + if (tableData.getDatasource().size() > 5) { + textResponse.append("... 还有 ").append(tableData.getDatasource().size() - 5).append(" 条记录"); + } + + result.setResponse(textResponse.toString()); + } + break; + + case "chart": + // 图表格式:检查是否有数值数据 + boolean hasNumericData = false; + if (tableData.getDatasource().size() > 0) { + Map firstRow = tableData.getDatasource().get(0); + for (Object value : firstRow.values()) { + if (value instanceof Number) { + hasNumericData = true; + break; + } + } + } + + if (hasNumericData) { + // 转换为图表数据格式 + DynamicDataVo.ChartData chartData = new DynamicDataVo.ChartData(); + + // 图表配置 + Map options = new HashMap<>(); + options.put("title", "数据统计图表"); + options.put("type", "bar"); + options.put("xAxis", "类别"); + options.put("yAxis", "数值"); + chartData.setOptions(options); + chartData.setDataset(tableData.getDatasource()); + + result.setResponse(chartData); + } else { + // 如果没有数值数据,转为文本格式 + renderType = "text"; + result.setResponse("数据不适合图表展示,转为文本格式显示"); + } + break; + + case "fix": + // 复合格式:返回数组格式 + List> fixData = new ArrayList<>(); + + // 添加表格数据 + Map tableBlock = new HashMap<>(); + tableBlock.put("meta", result.getMeta()); + tableBlock.put("response", tableData); + tableBlock.put("renderType", "table"); + fixData.add(tableBlock); + + // 添加统计信息 + Map statsBlock = new HashMap<>(); + Map statsMeta = new HashMap<>(); + + List> statsFields = new ArrayList<>(); + Map totalField = new HashMap<>(); + totalField.put("fieldName", "total"); + totalField.put("label", "总记录数"); + totalField.put("type", "number"); + statsFields.add(totalField); + + Map descField = new HashMap<>(); + descField.put("fieldName", "description"); + descField.put("label", "描述"); + descField.put("type", "string"); + statsFields.add(descField); + + statsMeta.put("fields", statsFields); + statsBlock.put("meta", statsMeta); + + Map statsResponse = new HashMap<>(); + statsResponse.put("total", tableData.getDatasource().size()); + statsResponse.put("description", "查询结果统计"); + statsBlock.put("response", statsResponse); + statsBlock.put("renderType", "text"); + fixData.add(statsBlock); + + result.setResponse(fixData); + break; + + case "table": + default: + // 表格格式:保持原有格式 + // 如果数据为空,转为文本 + if (tableData.getDatasource() == null || tableData.getDatasource().isEmpty()) { + renderType = "text"; + result.setResponse("暂无相关数据"); + } + break; + } + } + + result.setRenderType(renderType); + return result; + } } \ No newline at end of file diff --git a/ruoyi-oa/src/main/java/com/ruoyi/oa/service/impl/DatabaseQueryServiceImpl.java b/ruoyi-oa/src/main/java/com/ruoyi/oa/service/impl/DatabaseQueryServiceImpl.java index 45c7bfe..aac09be 100644 --- a/ruoyi-oa/src/main/java/com/ruoyi/oa/service/impl/DatabaseQueryServiceImpl.java +++ b/ruoyi-oa/src/main/java/com/ruoyi/oa/service/impl/DatabaseQueryServiceImpl.java @@ -107,13 +107,19 @@ public class DatabaseQueryServiceImpl implements IDatabaseQueryService { List> data = executeQuery(sql); DynamicDataVo result = new DynamicDataVo(); - result.setData(data); - + + // 设置渲染类型为表格 + result.setRenderType("table"); + + // 构建表格数据 + DynamicDataVo.TableData tableData = new DynamicDataVo.TableData(); + // 如果需要元信息,则生成字段元信息 if (includeMeta && tableName != null) { List columns = getTableColumns(tableName); DynamicDataVo.Meta meta = new DynamicDataVo.Meta(); List fields = new ArrayList<>(); + List> columnsList = new ArrayList<>(); for (TableColumnVo column : columns) { DynamicDataVo.Field field = new DynamicDataVo.Field(); @@ -122,11 +128,24 @@ public class DatabaseQueryServiceImpl implements IDatabaseQueryService { field.setType(getFieldType(column)); field.setFormat(getFieldFormat(column)); fields.add(field); + + // 构建列信息 + Map columnInfo = new HashMap<>(); + columnInfo.put("field", column.getColumnName()); + columnInfo.put("title", column.getColumnComment() != null ? column.getColumnComment() : column.getColumnName()); + columnInfo.put("type", getFieldType(column)); + columnInfo.put("format", getFieldFormat(column)); + columnsList.add(columnInfo); } meta.setFields(fields); result.setMeta(meta); + tableData.setColumns(columnsList); } + + // 设置数据源 + tableData.setDatasource(data); + result.setResponse(tableData); return result; }