找回密码
 立即注册
首页 业界区 安全 Springboot后端管理项目+AI对话式分析

Springboot后端管理项目+AI对话式分析

骂治并 2025-5-30 10:57:11
Springboot后端管理项目+AI对话式分析

什么叫对话式分析

‌对话式分析‌(Conversational Analytics)是一种通过‌自然语言交互‌实现数据探索的技术。用户无需掌握SQL或编程技能,像与真人对话一样用日常语言提问(如:"上季度华东区哪些产品的退货率超过5%?"),系统自动解析语义、生成查询、返回可视化结果。
1.png

与传统分析方式的本质区别‌


  • 对比维度‌        ‌传统分析‌        ‌对话式分析‌
  • 交互方式‌        编写SQL/使用固定报表模板        直接输入自然语言问题
  • 响应速度‌        需求排期→开发→测试(3天+)        实时响应(秒级)
  • 技术门槛‌        需懂数据库结构和技术术语        普通业务人员零门槛操作
  • 灵活度‌        受限于预设指标和维度        支持任意组合的即时分析
Springboot + 智普AI 实现

智普AI-官网:https://bigmodel.cn/dev/activities/free/glm-4-flash
2.png

对话式-数据库自动分析:核心就是数据库表和字段注释一定要清楚

示例数据库(一定要中文描述足够详细)
  1. CREATE TABLE `sys_dept` (
  2.   `dept_id` bigint(20) NOT NULL COMMENT '部门id',
  3.   `tenant_id` varchar(20) DEFAULT '000000' COMMENT '租户编号',
  4.   `parent_id` bigint(20) DEFAULT '0' COMMENT '父部门id',
  5.   `ancestors` varchar(500) DEFAULT '' COMMENT '祖级列表',
  6.   `dept_name` varchar(30) DEFAULT '' COMMENT '部门名称',
  7.   `dept_category` varchar(100) DEFAULT NULL COMMENT '部门类别编码',
  8.   `order_num` int(11) DEFAULT '0' COMMENT '显示顺序',
  9.   `leader` bigint(20) DEFAULT NULL COMMENT '负责人',
  10.   `phone` varchar(11) DEFAULT NULL COMMENT '联系电话',
  11.   `email` varchar(50) DEFAULT NULL COMMENT '邮箱',
  12.   `status` char(1) DEFAULT '0' COMMENT '部门状态(0正常 1停用)',
  13.   `del_flag` char(1) DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)',
  14.   `create_dept` bigint(20) DEFAULT NULL COMMENT '创建部门',
  15.   `create_by` bigint(20) DEFAULT NULL COMMENT '创建者',
  16.   `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  17.   `update_by` bigint(20) DEFAULT NULL COMMENT '更新者',
  18.   `update_time` datetime DEFAULT NULL COMMENT '更新时间',
  19.   PRIMARY KEY (`dept_id`) USING BTREE
  20. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT='部门表';
  21. CREATE TABLE `sys_user` (
  22.   `user_id` bigint(20) NOT NULL COMMENT '用户ID',
  23.   `tenant_id` varchar(20) DEFAULT '000000' COMMENT '租户编号',
  24.   `dept_id` bigint(20) DEFAULT NULL COMMENT '部门ID',
  25.   `user_name` varchar(30) NOT NULL COMMENT '用户账号',
  26.   `nick_name` varchar(30) NOT NULL COMMENT '用户昵称',
  27.   `user_type` varchar(10) DEFAULT 'sys_user' COMMENT '用户类型(sys_user系统用户)',
  28.   `email` varchar(50) DEFAULT '' COMMENT '用户邮箱',
  29.   `phone_number` varchar(11) DEFAULT '' COMMENT '手机号码',
  30.   `sex` char(1) DEFAULT '0' COMMENT '用户性别(0男 1女 2未知)',
  31.   `avatar` bigint(20) DEFAULT NULL COMMENT '头像地址',
  32.   `password` varchar(100) DEFAULT '' COMMENT '密码',
  33.   `status` char(1) DEFAULT '0' COMMENT '帐号状态(1正常 0停用)',
  34.   `del_flag` char(1) DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)',
  35.   `login_ip` varchar(128) DEFAULT '' COMMENT '最后登录IP',
  36.   `login_date` datetime DEFAULT NULL COMMENT '最后登录时间',
  37.   `create_dept` bigint(20) DEFAULT NULL COMMENT '创建部门',
  38.   `create_by` bigint(20) DEFAULT NULL COMMENT '创建者',
  39.   `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  40.   `update_by` bigint(20) DEFAULT NULL COMMENT '更新者',
  41.   `update_time` datetime DEFAULT NULL COMMENT '更新时间',
  42.   `remark` varchar(500) DEFAULT NULL COMMENT '备注',
  43.   PRIMARY KEY (`user_id`) USING BTREE
  44. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT='用户信息表';
  45. CREATE TABLE `sys_role` (
  46.   `role_id` bigint(20) NOT NULL COMMENT '角色ID',
  47.   `tenant_id` varchar(20) DEFAULT '000000' COMMENT '租户编号',
  48.   `role_name` varchar(30) NOT NULL COMMENT '角色名称',
  49.   `role_key` varchar(100) NOT NULL COMMENT '角色权限字符串',
  50.   `role_sort` int(11) NOT NULL COMMENT '显示顺序',
  51.   `data_scope` char(1) DEFAULT '1' COMMENT '数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限)',
  52.   `menu_check_strictly` tinyint(1) DEFAULT '1' COMMENT '菜单树选择项是否关联显示',
  53.   `dept_check_strictly` tinyint(1) DEFAULT '1' COMMENT '部门树选择项是否关联显示',
  54.   `status` char(1) NOT NULL COMMENT '角色状态(0正常 1停用)',
  55.   `del_flag` char(1) DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)',
  56.   `create_dept` bigint(20) DEFAULT NULL COMMENT '创建部门',
  57.   `create_by` bigint(20) DEFAULT NULL COMMENT '创建者',
  58.   `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  59.   `update_by` bigint(20) DEFAULT NULL COMMENT '更新者',
  60.   `update_time` datetime DEFAULT NULL COMMENT '更新时间',
  61.   `remark` varchar(500) DEFAULT NULL COMMENT '备注',
  62.   PRIMARY KEY (`role_id`) USING BTREE
  63. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT='角色信息表';
复制代码
实现流程


  • 用户发起请求

    • 用户通过 /ai/zhi_pu_qa 接口发起请求,传入 question 和 dbName 参数。

  • 获取数据库表名和注释

    • 调用 getTablesAndComments 方法,使用 SHOW CREATE TABLE 语句获取指定数据库的所有表名和注释。

  • SQL 语句示例:
  1.      SELECT TABLE_NAME, TABLE_COMMENT FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'test_admin_123';
  2.      
复制代码

  • 检查表名和注释是否为空

    • 如果 tablesAndComments 为空,返回“知识库未找到相关信息”。

  • 询问AI获取相关表信息

    • 调用 handlerAiQuestion 方法,将表名和注释信息传递给 AI,询问与问题相关的表信息。
    • AI 返回的格式示例:

  1.      {
  2.        "choices": [
  3.          {
  4.            "finish_reason": "stop",
  5.            "index": 0,
  6.            "message": {
  7.              "content": "[{"tableName":"sys_user"}]",
  8.              "role": "assistant"
  9.            }
  10.          }
  11.        ],
  12.        "created": 1743232278,
  13.        "id": "202503291511179a664d1e8d0245d7",
  14.        "model": "glm-4-flash",
  15.        "request_id": "guo_tong_1743232383435",
  16.        "usage": {
  17.          "completion_tokens": 32,
  18.          "prompt_tokens": 498,
  19.          "total_tokens": 530
  20.        }
  21.      }
  22.      
复制代码

  • 检查AI返回是否为空

    • 如果 tableInfosJson 为空,返回“知识库未找到相关信息”。

  • 解析AI返回 的表信息

    • 调用 parseTableFromResponse 方法,从 AI 返回的 JSON 中解析出表名列表
    • 解析示例:

  1.      [{"tableName":"sys_user"}]
  2.      
复制代码

  • 解析后得到表名字符串:
  1.      sys_user
复制代码

  • 检查表信息是否为空

    • 如果 tableNames 为空,返回“知识库未找到相关信息”。

  • 获取表的DDL

    • 调用 getTableDdl 方法,根据解析出的表名列表获取每个表的建表语句(DDL)。
    • SQL 语句示例:

  1.      SHOW CREATE TABLE `test_admin_123`.`sys_user`;
  2.      
复制代码

  • 检查DDL是否为空

    • 如果 columnsAndComments 为空,返回“知识库未找到相关信息”。

  • 询问AI生成SQL

    • 调用 handlerAiQuestion 方法,将表的 DDL 信息传递给 AI,询问生成查询 SQL。
    • AI 返回的格式示例:

  1.       {
  2.         "choices": [
  3.           {
  4.             "finish_reason": "stop",
  5.             "index": 0,
  6.             "message": {
  7.               "content": "SELECT * FROM `sys_user` WHERE YEAR(`create_time`) = 2024",
  8.               "role": "assistant"
  9.             }
  10.           }
  11.         ],
  12.         "created": 1743232278,
  13.         "id": "202503291511179a664d1e8d0245d7",
  14.         "model": "glm-4-flash",
  15.         "request_id": "guo_tong_1743232383435",
  16.         "usage": {
  17.           "completion_tokens": 32,
  18.           "prompt_tokens": 498,
  19.           "total_tokens": 530
  20.         }
  21.       }
  22.       
复制代码

  • 检查AI返回是否为空

    • 如果 result 为空,返回“接口调用失败,请检查日志!”。

  • 解析AI返回的SQL

    • 调用 parseSqlFromResponse 方法,从 AI 返回的 JSON 中解析出 SQL 语句。
    • 解析示例:

  1.       {
  2.         "choices": [
  3.           {
  4.             "finish_reason": "stop",
  5.             "index": 0,
  6.             "message": {
  7.               "content": "SELECT * FROM `sys_user` WHERE YEAR(`create_time`) = 2024",
  8.               "role": "assistant"
  9.             }
  10.           }
  11.         ]
  12.       }
  13.       
复制代码

  • 解析后得到 SQL 语句:
  1.       SELECT * FROM `sys_user` WHERE YEAR(`create_time`) = 2024
复制代码

  • 检查SQL是否为空

    • 如果 sql 为空,返回“解析SQL失败,请检查AI返回的内容!”。

  • 执行SQL

    • 调用 executeSql 方法,使用 JDBC 执行解析出的 SQL 语句,获取查询结果。

  • 返回查询结果

    • 将查询结果转换为 JSON 字符串,返回给客户端。
    • 3.png


  1. [{
  2.         "tenant_id": "000000",
  3.         "del_flag": "0",
  4.         "create_time": 1731411199000,
  5.         "create_dept": 103,
  6.         "user_name": "test",
  7.         "sex": "0",
  8.         "login_date": 1731411199000,
  9.         "remark": "QQ",
  10.         "avatar": 10085,
  11.         "login_ip": "127.0.0.1",
  12.         "create_by": 1,
  13.         "password": "f379eaf3c831b04de153469d1bec345e",
  14.         "update_time": 1731411199000,
  15.         "user_type": "sys_user",
  16.         "user_id": 3,
  17.         "nick_name": "本部门及以下 密码666666",
  18.         "phone_number": "15888888888",
  19.         "dept_id": 108,
  20.         "update_by": 3,
  21.         "email": "crazyLionLi@163.com",
  22.         "status": "1"
  23. }, {
  24.         "tenant_id": "000000",
  25.         "del_flag": "0",
  26.         "create_time": 1731411199000,
  27.         "create_dept": 103,
  28.         "user_name": "test1",
  29.         "sex": "1",
  30.         "login_date": 1731411199000,
  31.         "remark": "CMD",
  32.         "avatar": 10086,
  33.         "login_ip": "127.0.0.1",
  34.         "create_by": 1,
  35.         "password": "f379eaf3c831b04de153469d1bec345e",
  36.         "update_time": 1731411199000,
  37.         "user_type": "sys_user",
  38.         "user_id": 4,
  39.         "nick_name": "仅本人 密码666666",
  40.         "phone_number": "15888888888",
  41.         "dept_id": 102,
  42.         "update_by": 4,
  43.         "email": "crazyLionLi@163.com",
  44.         "status": "1"
  45. }, {
  46.         "tenant_id": "000000",
  47.         "del_flag": "0",
  48.         "create_time": 1735371444000,
  49.         "create_dept": 103,
  50.         "user_name": "zhoujielun",
  51.         "sex": "1",
  52.         "login_date": 1731411199000,
  53.         "remark": "我是周杰伦,夜曲一响上台领奖",
  54.         "avatar": 10086,
  55.         "login_ip": "0:0:0:0:0:0:0:1",
  56.         "create_by": 1,
  57.         "password": "e10adc3949ba59abbe56e057f20f883e",
  58.         "update_time": 1735371444000,
  59.         "user_type": "sys_user",
  60.         "user_id": 1872909700790542337,
  61.         "nick_name": "周杰伦",
  62.         "phone_number": "14837983573",
  63.         "dept_id": 103,
  64.         "update_by": 1,
  65.         "email": "zhoujielun@163.com",
  66.         "status": "1"
  67. }, {
  68.         "tenant_id": "000000",
  69.         "del_flag": "0",
  70.         "create_time": 1735372046000,
  71.         "create_dept": 103,
  72.         "user_name": "wanglihong",
  73.         "sex": "1",
  74.         "login_date": 1731411199000,
  75.         "remark": "我是王力宏,爱错一响,立即登场",
  76.         "avatar": 10086,
  77.         "login_ip": "0:0:0:0:0:0:0:1",
  78.         "create_by": 1,
  79.         "password": "e10adc3949ba59abbe56e057f20f883e",
  80.         "update_time": 1735372046000,
  81.         "user_type": "sys_user",
  82.         "user_id": 1872912222804516866,
  83.         "nick_name": "王力宏",
  84.         "phone_number": "15837357332",
  85.         "dept_id": 103,
  86.         "update_by": 1,
  87.         "email": "wanglihong@163.com",
  88.         "status": "1"
  89. }, {
  90.         "tenant_id": "000000",
  91.         "del_flag": "0",
  92.         "create_time": 1735372147000,
  93.         "create_dept": 103,
  94.         "user_name": "huachenyu",
  95.         "sex": "1",
  96.         "login_date": 1731411199000,
  97.         "remark": "我是华晨宇,华语乐坛永远的神",
  98.         "avatar": 10086,
  99.         "login_ip": "0:0:0:0:0:0:0:1",
  100.         "create_by": 1,
  101.         "password": "e10adc3949ba59abbe56e057f20f883e",
  102.         "update_time": 1735711391000,
  103.         "user_type": "sys_user",
  104.         "user_id": 1872912647666540546,
  105.         "nick_name": "华晨宇",
  106.         "phone_number": "15837557332",
  107.         "dept_id": 103,
  108.         "update_by": 1,
  109.         "email": "huachenyu@163.com",
  110.         "status": "1"
  111. }, {
  112.         "tenant_id": "000000",
  113.         "del_flag": "0",
  114.         "create_time": 1735372174000,
  115.         "create_dept": 103,
  116.         "user_name": "dengziqi",
  117.         "sex": "1",
  118.         "login_date": 1731411199000,
  119.         "remark": "我是邓紫棋,泡沫一响,立即登场",
  120.         "avatar": 10086,
  121.         "login_ip": "0:0:0:0:0:0:0:1",
  122.         "create_by": 1,
  123.         "password": "e10adc3949ba59abbe56e057f20f883e",
  124.         "update_time": 1735372174000,
  125.         "user_type": "sys_user",
  126.         "user_id": 1872912759570571265,
  127.         "nick_name": "邓紫棋",
  128.         "phone_number": "15837557332",
  129.         "dept_id": 103,
  130.         "update_by": 1,
  131.         "email": "dengziqi@163.com",
  132.         "status": "1"
  133. }, {
  134.         "tenant_id": "000000",
  135.         "del_flag": "0",
  136.         "create_time": 1735372214000,
  137.         "create_dept": 103,
  138.         "user_name": "chenyixun",
  139.         "sex": "1",
  140.         "login_date": 1731411199000,
  141.         "remark": "我是陈奕迅,孤独患者一响,立即登场",
  142.         "avatar": 10086,
  143.         "login_ip": "0:0:0:0:0:0:0:1",
  144.         "create_by": 1,
  145.         "password": "e10adc3949ba59abbe56e057f20f883e",
  146.         "update_time": 1735372214000,
  147.         "user_type": "sys_user",
  148.         "user_id": 1872912929188225026,
  149.         "nick_name": "陈奕迅",
  150.         "phone_number": "15837557232",
  151.         "dept_id": 103,
  152.         "update_by": 1,
  153.         "email": "chenyixun@163.com",
  154.         "status": "1"
  155. }, {
  156.         "tenant_id": "000000",
  157.         "del_flag": "0",
  158.         "create_time": 1735372278000,
  159.         "create_dept": 103,
  160.         "user_name": "linjunjie",
  161.         "sex": "1",
  162.         "login_date": 1731411199000,
  163.         "remark": "我是林俊杰,江南一响,青春回响",
  164.         "avatar": 10086,
  165.         "login_ip": "0:0:0:0:0:0:0:1",
  166.         "create_by": 1,
  167.         "password": "e10adc3949ba59abbe56e057f20f883e",
  168.         "update_time": 1735372278000,
  169.         "user_type": "sys_user",
  170.         "user_id": 1872913195568472066,
  171.         "nick_name": "林俊杰",
  172.         "phone_number": "15837117232",
  173.         "dept_id": 103,
  174.         "update_by": 1,
  175.         "email": "linjunjie@163.com",
  176.         "status": "1"
  177. }, {
  178.         "tenant_id": "000000",
  179.         "del_flag": "0",
  180.         "create_time": 1735372341000,
  181.         "create_dept": 103,
  182.         "user_name": "layVueSuper",
  183.         "sex": "1",
  184.         "login_date": 1731411199000,
  185.         "remark": "我是layVueSuper,暮色回响",
  186.         "avatar": 10086,
  187.         "login_ip": "0:0:0:0:0:0:0:1",
  188.         "create_by": 1,
  189.         "password": "e10adc3949ba59abbe56e057f20f883e",
  190.         "update_time": 1735372341000,
  191.         "user_type": "sys_user",
  192.         "user_id": 1872913463194427394,
  193.         "nick_name": "layVueSuper",
  194.         "phone_number": "15837127232",
  195.         "dept_id": 103,
  196.         "update_by": 1,
  197.         "email": "layVueSuper@163.com",
  198.         "status": "1"
  199. }, {
  200.         "tenant_id": "000000",
  201.         "del_flag": "0",
  202.         "create_time": 1735372409000,
  203.         "create_dept": 103,
  204.         "user_name": "zhangyunjing",
  205.         "sex": "1",
  206.         "login_date": 1731411199000,
  207.         "remark": "我是张芸京,偏爱永不落席",
  208.         "avatar": 10086,
  209.         "login_ip": "0:0:0:0:0:0:0:1",
  210.         "create_by": 1,
  211.         "password": "e10adc3949ba59abbe56e057f20f883e",
  212.         "update_time": 1735372409000,
  213.         "user_type": "sys_user",
  214.         "user_id": 1872913747660513281,
  215.         "nick_name": "张芸京",
  216.         "phone_number": "15837137232",
  217.         "dept_id": 103,
  218.         "update_by": 1,
  219.         "email": "zhangyunjing@163.com",
  220.         "status": "1"
  221. }, {
  222.         "tenant_id": "000000",
  223.         "del_flag": "0",
  224.         "create_time": 1735372459000,
  225.         "create_dept": 103,
  226.         "user_name": "weilian",
  227.         "sex": "1",
  228.         "login_date": 1731411199000,
  229.         "remark": "我是韦礼安,如果可以-新星崛起",
  230.         "avatar": 10086,
  231.         "login_ip": "0:0:0:0:0:0:0:1",
  232.         "create_by": 1,
  233.         "password": "e10adc3949ba59abbe56e057f20f883e",
  234.         "update_time": 1735372459000,
  235.         "user_type": "sys_user",
  236.         "user_id": 1872913957228912641,
  237.         "nick_name": "韦礼安",
  238.         "phone_number": "15837137233",
  239.         "dept_id": 103,
  240.         "update_by": 1,
  241.         "email": "weilian@163.com",
  242.         "status": "1"
  243. }, {
  244.         "tenant_id": "000000",
  245.         "del_flag": "0",
  246.         "create_time": 1735372508000,
  247.         "create_dept": 103,
  248.         "user_name": "wangfei",
  249.         "sex": "1",
  250.         "login_date": 1731411199000,
  251.         "remark": "我是王菲,如果可以-最后的天后",
  252.         "avatar": 10086,
  253.         "login_ip": "0:0:0:0:0:0:0:1",
  254.         "create_by": 1,
  255.         "password": "e10adc3949ba59abbe56e057f20f883e",
  256.         "update_time": 1735372508000,
  257.         "user_type": "sys_user",
  258.         "user_id": 1872914162380709890,
  259.         "nick_name": "王菲",
  260.         "phone_number": "15837137234",
  261.         "dept_id": 103,
  262.         "update_by": 1,
  263.         "email": "wangfei@163.com",
  264.         "status": "1"
  265. }, {
  266.         "tenant_id": "000000",
  267.         "del_flag": "0",
  268.         "create_time": 1735372569000,
  269.         "create_dept": 103,
  270.         "user_name": "huge",
  271.         "sex": "1",
  272.         "login_date": 1731411199000,
  273.         "remark": "我是胡歌,仙剑-最后的古装",
  274.         "avatar": 10086,
  275.         "login_ip": "0:0:0:0:0:0:0:1",
  276.         "create_by": 1,
  277.         "password": "e10adc3949ba59abbe56e057f20f883e",
  278.         "update_time": 1735372569000,
  279.         "user_type": "sys_user",
  280.         "user_id": 1872914420015833090,
  281.         "nick_name": "胡歌",
  282.         "phone_number": "15837137236",
  283.         "dept_id": 103,
  284.         "update_by": 1,
  285.         "email": "huge@163.com",
  286.         "status": "1"
  287. }, {
  288.         "tenant_id": "000000",
  289.         "del_flag": "0",
  290.         "create_time": 1735372652000,
  291.         "create_dept": 103,
  292.         "user_name": "zhongli",
  293.         "sex": "1",
  294.         "login_date": 1731411199000,
  295.         "remark": "我是钟离,璃月-岩王帝君",
  296.         "avatar": 10086,
  297.         "login_ip": "0:0:0:0:0:0:0:1",
  298.         "create_by": 1,
  299.         "password": "e10adc3949ba59abbe56e057f20f883e",
  300.         "update_time": 1735372652000,
  301.         "user_type": "sys_user",
  302.         "user_id": 1872914765664231425,
  303.         "nick_name": "钟离",
  304.         "phone_number": "15837137536",
  305.         "dept_id": 103,
  306.         "update_by": 1,
  307.         "email": "zhongli@163.com",
  308.         "status": "1"
  309. }]
复制代码
代码示例:
  1. /**
  2.      * AI对话式分析同步调用
  3.      */
  4.     @RequestMapping("/zhi_pu_qa")
  5.     public String testInvoke(@RequestParam(value = "question", defaultValue = "2024年创建的用户信息有哪些?", required = false) String question,
  6.                              @RequestParam(value = "dbName", defaultValue = "test_admin_123", required = false) String dbName) {
  7.         // 获取指定数据库的所有表名和表注释
  8.         String tablesAndComments = getTablesAndComments(dbName);
  9.         String notFindMsg = "知识库未找到相关信息";
  10.         if (CharSequenceUtil.isBlank(tablesAndComments)) {
  11.             return notFindMsg;
  12.         }
  13.         // 询问Ai获取指定问题和表的相似的相关的表信息
  14.         String tableInfosJson = handlerAiQuestion(tablesAndComments, question, false);
  15.         if (CharSequenceUtil.isBlank(tableInfosJson)) {
  16.             return notFindMsg;
  17.         }
  18.         // 处理AI回答的数据获取实际用的表
  19.         String tableNames = parseTableFromResponse(tableInfosJson);
  20.         if (CharSequenceUtil.isBlank(tableNames)) {
  21.             return notFindMsg;
  22.         }
  23.         // 根据表名称--获取对应的表结构的列名和注释
  24.         String columnsAndComments = getTableDdl(dbName, tableNames);
  25.         // 将问题询问AI关联到那几张表--定位
  26.         String result = handlerAiQuestion(columnsAndComments, question, true);
  27.         if (result == null) {
  28.             return "接口调用失败,请检查日志!";
  29.         }
  30.         // 解析获取到的SQL
  31.         String sql = parseSqlFromResponse(result);
  32.         if (sql == null) {
  33.             return "解析SQL失败,请检查AI返回的内容!";
  34.         }
  35.         // 调用JDBC连接执行该SQL拿到结果
  36.         List<Map<String, Object>> resultList = executeSql(sql);
  37.         // 将结果转换为JSON字符串返回
  38.         return JSONUtil.toJsonStr(resultList);
  39.     }
复制代码
你还想要完整示例吧
  1. package com.gt.quality.controller;import cn.dev33.satoken.annotation.SaIgnore;import cn.hutool.core.collection.CollUtil;import cn.hutool.core.text.CharSequenceUtil;import cn.hutool.http.HttpResponse;import cn.hutool.http.HttpUtil;import cn.hutool.http.Method;import cn.hutool.json.JSONObject;import cn.hutool.json.JSONUtil;import com.gt.quality.config.ZhiPuAIConstant;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Value;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.RestController;import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;import java.sql.Connection;import java.sql.DriverManager;import java.sql.ResultSet;import java.sql.Statement;import java.util.ArrayList;import java.util.Arrays;import java.util.HashMap;import java.util.List;import java.util.Map;import java.util.Objects;import java.util.stream.Collectors;/** * 万里悲秋常作客,百年多病独登台 * * @author : makeJava */@RestController@RequestMapping("/ai")@Slf4j@SaIgnorepublic class ZhiPuAiController {    // 请自定义自己的业务id    private static final String REQUEST_ID_TEMPLATE = "guo_tong_%d";    private static final String COMPLETIONS_URL = "https://open.bigmodel.cn/api/paas/v4/chat/completions";    @Value("${spring.datasource.url}")    private String dbUrl;    @Value("${spring.datasource.username}")    private String dbUsername;    @Value("${spring.datasource.password}")    private String dbPassword;    /**
  2.      * AI对话式分析同步调用
  3.      */
  4.     @RequestMapping("/zhi_pu_qa")
  5.     public String testInvoke(@RequestParam(value = "question", defaultValue = "2024年创建的用户信息有哪些?", required = false) String question,
  6.                              @RequestParam(value = "dbName", defaultValue = "test_admin_123", required = false) String dbName) {
  7.         // 获取指定数据库的所有表名和表注释
  8.         String tablesAndComments = getTablesAndComments(dbName);
  9.         String notFindMsg = "知识库未找到相关信息";
  10.         if (CharSequenceUtil.isBlank(tablesAndComments)) {
  11.             return notFindMsg;
  12.         }
  13.         // 询问Ai获取指定问题和表的相似的相关的表信息
  14.         String tableInfosJson = handlerAiQuestion(tablesAndComments, question, false);
  15.         if (CharSequenceUtil.isBlank(tableInfosJson)) {
  16.             return notFindMsg;
  17.         }
  18.         // 处理AI回答的数据获取实际用的表
  19.         String tableNames = parseTableFromResponse(tableInfosJson);
  20.         if (CharSequenceUtil.isBlank(tableNames)) {
  21.             return notFindMsg;
  22.         }
  23.         // 根据表名称--获取对应的表结构的列名和注释
  24.         String columnsAndComments = getTableDdl(dbName, tableNames);
  25.         // 将问题询问AI关联到那几张表--定位
  26.         String result = handlerAiQuestion(columnsAndComments, question, true);
  27.         if (result == null) {
  28.             return "接口调用失败,请检查日志!";
  29.         }
  30.         // 解析获取到的SQL
  31.         String sql = parseSqlFromResponse(result);
  32.         if (sql == null) {
  33.             return "解析SQL失败,请检查AI返回的内容!";
  34.         }
  35.         // 调用JDBC连接执行该SQL拿到结果
  36.         List<Map<String, Object>> resultList = executeSql(sql);
  37.         // 将结果转换为JSON字符串返回
  38.         return JSONUtil.toJsonStr(resultList);
  39.     }    /**     * Description:     *     * @author: makeJava     * @date: 2025-03-29 16:55:35     * @return:     */    private static String handlerAiQuestion(String params, String question, boolean createSql) {        Map body = new HashMap();        body.put("model", "glm-4-flash");        Object messages;        if (createSql) {            messages = buildSqlParam(params, question);        } else {            messages = getSelectTableNames(params, question);        }        body.put("messages", messages);        body.put("request_id", String.format(REQUEST_ID_TEMPLATE, System.currentTimeMillis()));        body.put("do_sample", true);        body.put("stream", false);        body.put("temperature", 0.95);        body.put("max_tokens", 4095);        Map responseFormat = new HashMap();        responseFormat.put("type", "json_object");        body.put("response_format", responseFormat);        // function、retrieval、web_search。        body.put("type", "web_search");        String result = "null";        try (HttpResponse response = HttpUtil.createRequest(Method.POST, COMPLETIONS_URL)                .body(JSONUtil.toJsonStr(body))                .header("Authorization", "Bearer " + ZhiPuAIConstant.ZHI_PU_AI_API_KEY)                .execute()) {            // 使用 try-with-resources 确保资源自动关闭            result = response.body();        } catch (Exception e) {            log.error("调用接口失败: {}", e.getMessage(), e);            return null;        }        log.info("调用接口返回: {}", result);        return result;    }    /**     * 根据问题获取关联到要查询的表名     */    private static Object getSelectTableNames(String tableNames, String question) {        List messages = new ArrayList();        Map msgItem = new HashMap();        msgItem.put("role", "user");        msgItem.put("content", "请你作为数据分析师," +                               "现在数据库里面存在表这些:" + tableNames + ";帮助查询出" + question + ";具体输入格式返回为JSON,示例->[{'tableName':'table01'},{'tableName':'table02'}]]。");        messages.add(msgItem);        return messages;    }    private static List buildSqlParam(String tableInfos, String question) {        List messages = new ArrayList();        Map msgItem = new HashMap();        msgItem.put("role", "user");        msgItem.put("content", "请你作为数据分析师," +                               "现在数据库的表结构是这样:" + tableInfos + ";帮助查询出" + question + ",具体输入的内容为标准的SQL即可。");        messages.add(msgItem);        return messages;    }    /**     * 获取数据库的表名和注释     *     * @param databaseName 数据库名称     * @return 表名和注释的JSON字符串     */    public String getTablesAndComments(String databaseName) {        String sql = "SELECT TABLE_NAME, TABLE_COMMENT FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = '" + databaseName + "'";        List resultList = executeSql(sql);        return JSONUtil.toJsonStr(resultList);    }    /**     * 获取指定表的列名和注释     *     * @param tableName 表名     * @return 列名和注释的JSON字符串     */    public String getColumnsAndComments(String databaseName, String tableName) {        String sql = "SELECT COLUMN_NAME, COLUMN_COMMENT FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = '" + databaseName + "' AND TABLE_NAME = '" + tableName + "'";        List resultList = executeSql(sql);        return JSONUtil.toJsonStr(resultList);    }    /**     * 从AI返回的JSON中解析表信息     *     * @param response AI返回的JSON字符串     * @return 解析后的表信息字符串     */    @SuppressWarnings("unchecked")    private String parseTableFromResponse(String response) {        try {            Map responseMap = JSONUtil.toBean(response, Map.class);            List choices = (List) responseMap.get("choices");            if (choices != null && !choices.isEmpty()) {                Map choice = choices.get(0);                Map message = (Map) choice.get("message");                if (message != null) {                    String content = (String) message.get("content");                    if (content.contains("[")) {                        if (content.startsWith("\n")) {                            content = content.replace("\n", "");                        }                        List list = JSONUtil.toList(content, JSONObject.class);                        return list.stream()                                .map(jsonObject -> jsonObject.getStr("tableName"))                                .collect(Collectors.joining(","));                    }                    log.info("解析JSON---->: {}", content);                }            }        } catch (Exception e) {            log.error("解析JSON失败: {}", e.getMessage(), e);        }        return null;    }    /**     * 从AI返回的JSON中解析SQL     *     * @param response AI返回的JSON字符串     * @return 解析后的SQL字符串     */    @SuppressWarnings("unchecked")    private String parseSqlFromResponse(String response) {        try {            Map responseMap = JSONUtil.toBean(response, Map.class);            List choices = (List) responseMap.get("choices");            if (choices != null && !choices.isEmpty()) {                Map choice = choices.get(0);                Map message = (Map) choice.get("message");                if (message != null) {                    String content = (String) message.get("content");                    if (content != null) {                        if (content.startsWith("\n{")) {                            Map contentMap = JSONUtil.toBean((String) message.get("content"), Map.class);                            Object answer = contentMap.get("answer");                            if (answer != null) {                                return answer.toString();                            }                            Object sqlQuery = contentMap.get("sql_query");                            if (sqlQuery != null){                                return sqlQuery.toString();                            }                            Object query = contentMap.get("query");                            if (query != null){                                return query.toString();                            }                            return contentMap.toString();                        }                        content = content.replace("sql\n", "");                        return content;                    }                }            }        } catch (Exception e) {            log.error("解析JSON失败: {}", e.getMessage(), e);        }        return null;    }    /**     * 获取指定表的建表语句DDL     *     * @param databaseName 数据库名称     * @param tableNameStr 表名     * @return 建表语句的JSON字符串     */    public String getTableDdl(String databaseName, String tableNameStr) {        // 干扰表白名单--屏蔽掉        List whiteList = Arrays.asList("sys_role_dept", "sys_role_menu", "sys_oper_log", "file_export_template");        String[] split = tableNameStr.split(",");        StringBuilder stringBuilder = new StringBuilder();        List sqlList = new ArrayList();        for (String tableName : split) {            // 屏蔽掉干扰表            if (whiteList.contains(tableName)) {                continue;            }            String sql = "SHOW CREATE TABLE `" + databaseName + "`.`" + tableName + "`";            sqlList.add(sql);        }        List resultList = executeSqlList(sqlList);        if (CollUtil.isNotEmpty(resultList)) {            for (Map mapRow : resultList) {                String createTableStatement = (String) mapRow.get("Create Table");                stringBuilder.append(createTableStatement);            }            return stringBuilder.toString();        }        return null;    }    /**     * 执行SQL语句并返回结果     *     * @param sqlList SQL语句     * @return 结果列表     */    private List executeSqlList(List sqlList) {        List resultList = new ArrayList();        try (Connection connection = DriverManager.getConnection(dbUrl, dbUsername, dbPassword);             Statement statement = connection.createStatement();        ) {            for (String sql : sqlList) {                ResultSet resultSet = statement.executeQuery(sql);                int columnCount = resultSet.getMetaData().getColumnCount();                while (resultSet.next()) {                    Map row = new HashMap();                    for (int i = 1; i      */    private List executeSql(String sql) {        List resultList = new ArrayList();        try (Connection connection = DriverManager.getConnection(dbUrl, dbUsername, dbPassword);             Statement statement = connection.createStatement();             ResultSet resultSet = statement.executeQuery(sql)) {            int columnCount = resultSet.getMetaData().getColumnCount();            while (resultSet.next()) {                Map row = new HashMap();                for (int i = 1; i 只有数据库里面有问题相关的表:就自动去找表,自动生成SQL:可以多张表单张表都可以哟[/size]
  40. [size=4]这里使用了多表联查的问题:[/size]
  41. [size=3]实际SQL :[/size]
  42. [indent][b]SELECT d.dept_name FROM sys_user u JOIN sys_dept d ON u.dept_id = d.dept_id WHERE u.nick_name = '王力宏'[/b]
  43. [/indent][size=3]智普AI的返回的结果[/size]
  44. [code]{        "choices": [{                "finish_reason": "stop",                "index": 0,                "message": {                        "content": "\n{\n  "answer": "SELECT d.dept_name FROM sys_user u JOIN sys_dept d ON u.dept_id = d.dept_id WHERE u.nick_name = '王力宏'"\n}\n",                        "role": "assistant"                }        }],        "created": 1743241826,        "id": "20250329175024947ec2101b9e4442",        "model": "glm-4-flash",        "request_id": "guo_tong_1743241930598",        "usage": {                "completion_tokens": 46,                "prompt_tokens": 909,                "total_tokens": 955        }}
复制代码
4.png

这里再来秀一波 :用户昵称叫王多鱼的这个人的角色名称?

SELECT r.role_name FROM sys_user u INNER JOIN sys_user_role ur ON u.user_id = ur.user_id INNER JOIN sys_role r ON ur.role_id = r.role_id WHERE u.nick_name = '王多鱼'

5.png

看看返回的
  1. {
  2.         "choices": [{
  3.                 "finish_reason": "stop",
  4.                 "index": 0,
  5.                 "message": {
  6.                         "content": "\n{\n  "answer": "SELECT d.dept_name FROM sys_user u JOIN sys_dept d ON u.dept_id = d.dept_id WHERE u.nick_name = '王力宏'"\n}\n",
  7.                         "role": "assistant"
  8.                 }
  9.         }],
  10.         "created": 1743241826,
  11.         "id": "20250329175024947ec2101b9e4442",
  12.         "model": "glm-4-flash",
  13.         "request_id": "guo_tong_1743241930598",
  14.         "usage": {
  15.                 "completion_tokens": 46,
  16.                 "prompt_tokens": 909,
  17.                 "total_tokens": 955
  18.         }
  19. }
复制代码
出处:http://www.cnblogs.com/gtnotgod】/个性签名:独学而无友,则孤陋而寡闻。做一个灵魂有趣的人!
如果觉得这篇文章对你有小小的帮助的话,记得在右下角点个“推荐”哦,博主在此感谢!
Java入门到入坟
万水千山总是情,打赏一分行不行,所以如果你心情还比较高兴,也是可以扫码打赏博主,哈哈哈(っ•̀ω•́)っ✎⁾⁾!

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册