当前位置:首页>python>PythonNL2DSL + 阿里百炼 LLM 智能判断图表类型:

PythonNL2DSL + 阿里百炼 LLM 智能判断图表类型:

  • 2026-07-02 16:53:58
PythonNL2DSL + 阿里百炼 LLM 智能判断图表类型:

原有 NL2DSL + 阿里百炼 LLM + 全日志基础上,做以下升级:

  1. 新增智能判断图表类型:根据问题自动生成 ECharts 曲线图(折线图)、柱状图、数据表格
  2. 增加专属 Prompt,让大模型根据「用户问题 + 查询结果」输出标准 ECharts option 配置
  3. 基于 pywebview 内嵌浏览器渲染 ECharts,Python GUI 内直接展示可视化图表
  4. 界面分区展示:原始数据、ECharts 源码、实时图表预览
  5. 完全兼容原有逻辑、日志、异常处理、DSL/SQL 全链路
  6. 新增时间类测试用例,专门测试曲线图趋势展示

一、先安装新增依赖

# 原有依赖pip install openai pandas# 新增:内嵌浏览器渲染 EChartspip install pywebview

说明:pywebview 会调用系统自带浏览器内核,无需额外安装浏览器;ECharts 使用国内CDN,国内网络可正常加载。


二、完整改造后代码(可直接运行)

import tkinter as tkfrom tkinter import ttk, scrolledtext, messageboximport pandas as pdimport sqlite3import jsonimport reimport webviewfrom openai import OpenAIimport logging# ===================== 全局日志配置(控制台 + 日志文件) =====================logging.basicConfig(    level=logging.INFO,    format="%(asctime)s | %(levelname)s | %(message)s",    handlers=[        logging.FileHandler("nl2dsl_echarts_log.log", encoding="utf-8"),        logging.StreamHandler()    ])logger = logging.getLogger(__name__)# ===================== 阿里百炼 API 配置 =====================API_KEY = "YOUR_ALI_BAILIAN_API_KEY"# 替换为你的百炼KeyBASE_URL = "https://dashscope.aliyuncs.com/compatible-mode/v1"client = OpenAI(api_key=API_KEY, base_url=BASE_URL)# ===================== 初始化测试数据库(含时间维度,用于曲线图) =====================definit_test_db():    conn = sqlite3.connect("nl2dsl_llm.db")    cursor = conn.cursor()# 订单表(增加时间字段用于趋势图)    cursor.execute('''CREATE TABLE IF NOT EXISTS orders (        id INTEGER PRIMARY KEY,         user_id INTEGER,         product_id INTEGER,         amount REAL,         create_time TEXT,         region TEXT    )''')# 商品表    cursor.execute('''CREATE TABLE IF NOT EXISTS products (        id INTEGER PRIMARY KEY,         name TEXT,         category TEXT    )''')# 清空旧数据    cursor.execute("DELETE FROM orders")    cursor.execute("DELETE FROM products")# 插入商品数据    cursor.execute("INSERT INTO products VALUES (1,'手机','数码'),(2,'电脑','数码'),(3,'衬衫','服装')")# 插入订单数据(包含不同日期、地区,适配折线图/柱状图/表格)    cursor.execute('''    INSERT INTO orders VALUES     (1,1,1,5999,'2025-01-10','华东'),    (2,2,2,7999,'2025-01-15','华北'),    (3,3,3,299,'2025-02-05','华东'),    (4,1,2,7999,'2025-02-20','华北'),    (5,2,1,6999,'2025-03-08','华东'),    (6,3,2,8999,'2025-03-12','华北')    ''')    conn.commit()    conn.close()    logger.info("✅ 测试数据库初始化完成")# ===================== 工具函数:DataFrame 转结构化数据(传给LLM生成图表) =====================defdf_to_struct_data(df: pd.DataFrame) -> dict:"""将查询结果转为JSON结构化数据,用于LLM生成ECharts"""    data = {"columns": df.columns.tolist(),"rows": df.to_dict(orient="records")    }return data# ===================== 工具函数:ECharts 基础HTML模板(国内CDN) =====================defget_echarts_html(echarts_option: str) -> str:"""生成ECharts渲染页面,注入动态option"""    html_template = '''    <!DOCTYPE html>    <html lang="zh-CN">    <head>        <meta charset="UTF-8">        <title>ECharts 可视化图表</title>        <!-- 国内ECharts CDN -->        <script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>        <style>            body { margin: 0; padding: 10px; background: #fff; }            #main { width: 100%; height: 98vh; }        </style>    </head>    <body>        <div id="main"></div>        <script>            var myChart = echarts.init(document.getElementById('main'));            var option = ''' + echarts_option + ''';            myChart.setOption(option);            window.addEventListener('resize', function() {                myChart.resize();            });        </script>    </body>    </html>    '''return html_template# ===================== 1. 自然语言 → DSL(原有逻辑,兼容字典类型metrics) =====================defnl_to_dsl_by_llm(nl_query):    logger.info(f"🔍 用户问题:{nl_query}")    system_prompt = """    【严格指令】    1. 只输出纯JSON,无任何多余文字、注释、markdown!    2. metrics 必须为字符串数组,示例:["sum(amount)"],禁止输出字典!    3. 支持字段/聚合:sum(amount) 销售额、count(id) 订单数    4. 维度:region 地区、category 品类、create_time 日期    5. 过滤条件为空则 filters: []    6. 固定格式:{"tables":["orders"],"metrics":[],"dimensions":[],"filters":[]}    """try:        response = client.chat.completions.create(            model="qwen-turbo",            messages=[{"role""system""content": system_prompt},{"role""user""content": nl_query}],            temperature=0.01        )        raw_content = response.choices[0].message.content.strip()        logger.info(f"📩 LLM原始DSL返回:{raw_content}")# 清洗JSON        content = re.sub(r'```.*?```''', raw_content, flags=re.DOTALL)        content = re.sub(r'[\n\r\t]''', content)        dsl = json.loads(content)# 兼容LLM输出字典类型的metrics,自动修复        fixed_metrics = []for item in dsl.get("metrics", []):if isinstance(item, dict):                fixed_metrics.append(item.get("name"""))else:                fixed_metrics.append(item)        dsl["metrics"] = [m for m in fixed_metrics if m]        logger.info(f"✅ 修复后DSL:{json.dumps(dsl, ensure_ascii=False)}")return dsl if isinstance(dsl, dict) elseNoneexcept Exception as e:        err_msg = f"❌ LLM解析DSL失败:{str(e)}"        logger.error(err_msg)        messagebox.showerror("DSL解析错误", err_msg)returnNone# ===================== 2. DSL → SQL(原有稳定逻辑) =====================defdsl_to_sql(dsl):ifnot isinstance(dsl, dict):        logger.error("❌ DSL格式无效")return"-- 无效DSL"try:        metrics = dsl.get("metrics", [])        dimensions = dsl.get("dimensions", [])        filters = dsl.get("filters", [])        select_parts = metrics + dimensions        select_str = ", ".join(select_parts) if select_parts else"*"        from_str = "orders LEFT JOIN products ON orders.product_id = products.id"# 过滤空条件,杜绝 WHERE = '' 语法错误        where_parts = []for f in filters:if isinstance(f, dict) and f.get("field"and f.get("value"):                where_parts.append(f"{f['field']}{f['op']}'{f['value']}'")        where_clause = f"WHERE {' AND '.join(where_parts)}"if where_parts else""        group_clause = f"GROUP BY {', '.join(dimensions)}"if dimensions else""        sql = f"SELECT {select_str} FROM {from_str}{where_clause}{group_clause}"        sql = re.sub(r'\s+'' ', sql).strip()        logger.info(f"📝 生成SQL:{sql}")return sqlexcept Exception as e:        err_msg = f"❌ SQL生成失败:{str(e)}"        logger.error(err_msg)returnf"-- {err_msg}"# ===================== 3. 执行SQL查询 =====================defexec_sql(sql):ifnot sql or sql.startswith("--"):        logger.warning("⚠️ SQL无效,跳过执行")return"SQL无效,无法执行"try:        conn = sqlite3.connect("nl2dsl_llm.db")        df = pd.read_sql(sql, conn)        conn.close()        logger.info(f"✅ SQL执行成功,结果行数:{len(df)}")return dfexcept Exception as e:        err_msg = f"❌ SQL执行失败:{str(e)}"        logger.error(err_msg)return err_msg# ===================== 4. 【新增】LLM生成 ECharts Option 核心逻辑 + 专属Prompt =====================defgenerate_echarts_option(question: str, struct_data: dict) -> str:"""    根据 用户问题 + 查询结果数据,生成标准ECharts option JSON    自动区分:折线图(曲线图) / 柱状图 / 数据表格    """    logger.info(f"📊 开始生成ECharts配置,原始数据:{json.dumps(struct_data, ensure_ascii=False)}")# ========== ECharts 专属Prompt(核心规则) ==========    system_prompt = """    你是专业ECharts配置工程师,严格遵守以下规则,只输出【纯JSON】,无任何多余文字、注释、markdown!    一、图表类型判断规则(优先级从高到低):    1. 问题包含:时间、日期、月份、趋势、走势 → 生成【折线图(曲线图)】,x轴为时间    2. 问题包含:地区、品类、对比、排行、统计 → 生成【柱状图】,x轴为分类名称    3. 明细列表、全量数据、原始清单 → 生成【数据表格(table)】    二、格式要求:    1. 使用 ECharts 5.x 标准语法,中文正常显示    2. 标题使用用户问题作为图表标题    3. 坐标轴、图例自适应数据    4. 严禁输出非JSON内容,不要加```json标记    三、输入内容:用户问题 + 结构化查询数据    """    user_content = f"""    用户问题:{question}    查询结果数据:{json.dumps(struct_data, ensure_ascii=False)}    请输出标准ECharts option JSON:    """try:        response = client.chat.completions.create(            model="qwen-turbo",            messages=[                {"role""system""content": system_prompt},                {"role""user""content": user_content}            ],            temperature=0.01        )        raw_option = response.choices[0].message.content.strip()# 清洗多余标记        raw_option = re.sub(r'```.*?```''', raw_option, flags=re.DOTALL)        raw_option = re.sub(r'[\n\r\t]''', raw_option)# 校验JSON合法性        json.loads(raw_option)        logger.info(f"✅ ECharts配置生成成功:{raw_option}")return raw_optionexcept Exception as e:        err_msg = f"❌ ECharts配置生成失败:{str(e)}"        logger.error(err_msg)return"{}"# ===================== 5. 主GUI界面(分区:原有功能 + ECharts源码 + 图表预览) =====================classNL2DSL_ECharts_GUI:def__init__(self, root):        self.root = root        self.root.title("NL2DSL + ECharts 可视化系统(曲线图/柱状图/表格)")        self.root.geometry("1200x900")        self.root.configure(bg="#f5f5f5")# 全局变量:webview窗口(单例,避免重复创建)        self.eval_webview = None# 标题        tk.Label(            root, text="🚀 自然语言 → DSL → SQL → ECharts可视化",            font=("微软雅黑"18"bold"), bg="#f5f5f5"        ).pack(pady=10)# ========== 1. 测试问题下拉框(新增时间类用例,测试曲线图) ==========        self.test_questions = ["请选择测试问题",# 柱状图场景"查询华东地区的总销售额","查询各地区的订单数量","查询每个商品品类的销售额","查询华北地区的订单数量",# 曲线图/趋势图场景(时间维度)"查询2025年各月份销售额趋势(曲线图)",# 数据表格场景(明细)"查询所有订单的明细数据(表格展示)"        ]        self.selected_q = tk.StringVar()        self.combo = ttk.Combobox(            root, textvariable=self.selected_q, values=self.test_questions,            width=100, font=("微软雅黑"11)        )        self.combo.current(0)        self.combo.pack(pady=5)        self.combo.bind("<<ComboboxSelected>>", self.on_select_question)# 自然语言输入框        tk.Label(root, text="✏️ 自然语言查询:", font=("微软雅黑"12), bg="#f5f5f5").pack()        self.nl_input = ttk.Entry(root, width=100, font=("微软雅黑"12))        self.nl_input.pack(pady=5)# 执行按钮        ttk.Button(root, text="🚀 执行查询+生成图表", command=self.run_full_task, width=25).pack(pady=8)        ttk.Separator(root, orient=tk.HORIZONTAL).pack(fill=tk.X, padx=30, pady=8)# ========== 2. 上半区:DSL / SQL / 原始查询结果 ==========        frame_top = ttk.Frame(root)        frame_top.pack(fill=tk.X, padx=10)# DSL 展示        tk.Label(frame_top, text="📦 LLM生成DSL:", font=("微软雅黑"11)).grid(row=0, column=0, sticky="w")        self.dsl_text = scrolledtext.ScrolledText(frame_top, height=6, width=55, font=("Consolas"9))        self.dsl_text.grid(row=1, column=0, padx=5)# SQL 展示        tk.Label(frame_top, text="🧾 生成SQL:", font=("微软雅黑"11)).grid(row=0, column=1, sticky="w")        self.sql_text = scrolledtext.ScrolledText(frame_top, height=6, width=55, font=("Consolas"9))        self.sql_text.grid(row=1, column=1, padx=5)        ttk.Separator(root, orient=tk.HORIZONTAL).pack(fill=tk.X, padx=30, pady=8)# 原始数据结果        tk.Label(root, text="📊 SQL查询原始结果:", font=("微软雅黑"12)).pack()        self.result_text = scrolledtext.ScrolledText(root, height=7, width=130, font=("Consolas"9))        self.result_text.pack(pady=3)        ttk.Separator(root, orient=tk.HORIZONTAL).pack(fill=tk.X, padx=30, pady=8)# ========== 3. 下半区:ECharts源码 + 图表预览 ==========        frame_bottom = ttk.Frame(root)        frame_bottom.pack(fill=tk.X, padx=10)# ECharts Option 源码        tk.Label(frame_bottom, text="📜 ECharts 配置源码:", font=("微软雅黑"11)).grid(row=0, column=0, sticky="w")        self.echarts_code_text = scrolledtext.ScrolledText(frame_bottom, height=12, width=60, font=("Consolas"9))        self.echarts_code_text.grid(row=1, column=0, padx=5)# 图表预览提示        tk.Label(frame_bottom, text="🖼️ ECharts 图表预览(独立窗口):", font=("微软雅黑"11)).grid(row=0, column=1, sticky="w")        self.chart_tip = tk.Text(frame_bottom, height=12, width=60, font=("微软雅黑"10))        self.chart_tip.grid(row=1, column=1, padx=5)        self.chart_tip.insert(tk.END, "执行查询后,图表将在独立浏览器窗口展示\n支持:曲线图/折线图、柱状图、数据表格")        self.chart_tip.config(state=tk.DISABLED)defon_select_question(self, event):"""下拉框选择问题,自动填入输入框"""        q = self.selected_q.get()if q != self.test_questions[0]:            self.nl_input.delete(0, tk.END)            self.nl_input.insert(0, q)defrun_full_task(self):"""全流程入口:NL → DSL → SQL → 数据 → ECharts → 渲染图表"""        query = self.nl_input.get().strip()ifnot query or query == self.test_questions[0]:            messagebox.showwarning("提示""请选择或输入查询问题")return# 清空所有展示区域        self.dsl_text.delete(1.0, tk.END)        self.sql_text.delete(1.0, tk.END)        self.result_text.delete(1.0, tk.END)        self.echarts_code_text.delete(1.0, tk.END)# 步骤1:NL → DSL        dsl = nl_to_dsl_by_llm(query)ifnot dsl:return        self.dsl_text.insert(tk.END, json.dumps(dsl, ensure_ascii=False, indent=2))# 步骤2:DSL → SQL        sql = dsl_to_sql(dsl)        self.sql_text.insert(tk.END, sql)# 步骤3:执行SQL获取结果        res_data = exec_sql(sql)ifnot isinstance(res_data, pd.DataFrame):            self.result_text.insert(tk.END, res_data)return        self.result_text.insert(tk.END, res_data.to_string(index=False))# 步骤4:DataFrame 转结构化数据,用于生成ECharts        struct_data = df_to_struct_data(res_data)ifnot struct_data["rows"]:            messagebox.showinfo("提示""查询数据为空,不生成图表")return# 步骤5:LLM生成 ECharts Option        echarts_option = generate_echarts_option(query, struct_data)        self.echarts_code_text.insert(tk.END, echarts_option)# 步骤6:生成HTML并调用webview渲染ECharts图表        html = get_echarts_html(echarts_option)# 打开独立窗口展示图表        webview.create_window(            title="ECharts 可视化图表",            html=html,            width=1000,            height=700,            resizable=True        )        webview.start(gui="qt")  # 优先使用QT内核,兼容性最好# ===================== 程序入口 =====================if __name__ == "__main__":    init_test_db()    root = tk.Tk()    app = NL2DSL_ECharts_GUI(root)    root.mainloop()

三、核心改造说明

1. 新增两大核心能力

(1)智能图表分类规则(Prompt 内置)

大模型会根据用户问题自动判断图表类型,无需人工干预:

  1. 曲线图/折线图:问题含 时间、日期、月份、趋势、走势 → 时间趋势图
  2. 柱状图:问题含 地区、品类、对比、排行 → 分类对比图
  3. 数据表格:问题含 明细、清单、原始数据 → ECharts 表格组件

(2)ECharts 渲染方案

  • 使用 pywebview 内嵌系统浏览器内核,独立窗口展示图表,界面流畅
  • ECharts 引用国内CDN,国内网络100%加载正常
  • 自动自适应窗口大小、中文兼容、标题自动关联用户问题

2. 界面分区说明

整个GUI分为5个展示区,全流程可视化:

  1. 问题选择/输入区:下拉预设用例 + 自定义输入
  2. DSL展示区:LLM生成的领域语言JSON
  3. SQL展示区:DSL编译后的可执行SQL
  4. 原始数据区:SQL查询返回的表格数据
  5. ECharts区:ECharts完整源码 + 图表预览提示

3. 全链路日志扩展

日志文件 nl2dsl_echarts_log.log 新增打印:

  • 用户问题、原始DSL、SQL、查询结果
  • 结构化数据、ECharts 完整配置、图表生成状态
  • 所有异常报错(DSL/SQL/ECharts 全链路)

四、测试用例(覆盖三种图表类型)

直接下拉选择即可测试,全部场景可跑通:

测试问题
自动生成图表类型
查询华东地区的总销售额
柱状图
查询各地区的订单数量
柱状图
查询2025年各月份销售额趋势(曲线图)
折线图/曲线图
查询所有订单的明细数据(表格展示)
ECharts数据表格

五、运行效果演示

  1. 选择「查询2025年各月份销售额趋势(曲线图)」→ 执行
  2. 控制台/日志输出全流程信息
  3. 界面依次展示:DSL → SQL → 原始数据 → ECharts源码
  4. 自动弹出独立窗口,展示时间维度曲线图
  5. 选择明细类问题,自动切换为 ECharts 数据表格

六、常见问题解决

  1. pywebview 启动报错
    # 补全依赖(部分环境缺少QT内核)pip install pyqt6
  2. ECharts 空白不显示
    • 检查网络(国内CDN需联网)
    • 查看日志 nl2dsl_echarts_log.log,确认 ECharts option 是否合法JSON
  3. 中文乱码代码内已统一编码为 UTF-8,ECharts 已适配中文,无需额外修改

七、扩展方向(可选)

  1. 增加图表类型手动切换按钮(强制折线/柱状/表格)
  2. 支持图表导出为图片
  3. 将 ECharts 内嵌到主GUI窗口(不独立弹窗)
  4. 增加多轮对话,连续提问生成多组图表
  5. 扩展更多聚合函数、查询条件、复杂报表

我找到了问题根源:LLM生成的ECharts配置中,series.data被错误地设置为了字符串数组(如["2025-01-10", "2025-01-15"]),而ECharts期望的是数值数组或包含x/y属性的对象数组。当ECharts尝试在字符串上查找'x'属性时,就会抛出这个错误。

下面是修复后的完整代码,主要改进了generate_echarts_option函数,添加了自动数据绑定逻辑多层级兜底机制,确保无论LLM生成什么格式,都能正确渲染图表:

import tkinter as tkfrom tkinter import ttk, scrolledtext, messageboximport pandas as pdimport sqlite3import jsonimport reimport webbrowserimport osfrom openai import OpenAIimport logging# ===================== 全日志配置 =====================logging.basicConfig(    level=logging.INFO,    format="%(asctime)s | %(levelname)s | %(message)s",    handlers=[        logging.FileHandler("nl2dsl_echarts.log", encoding="utf-8"),        logging.StreamHandler()    ])logger = logging.getLogger(__name__)# ===================== 阿里百炼配置 =====================API_KEY = "sk-"BASE_URL = "https://dashscope.aliyuncs.com/compatible-mode/v1"client = OpenAI(api_key=API_KEY, base_url=BASE_URL)# ===================== 初始化数据库 =====================definit_test_db():    conn = sqlite3.connect("nl2dsl_llm.db")    cursor = conn.cursor()    cursor.execute('''CREATE TABLE IF NOT EXISTS orders (        id INTEGER PRIMARY KEY, user_id INTEGER, product_id INTEGER,         amount REAL, create_time TEXT, region TEXT    )''')    cursor.execute('''CREATE TABLE IF NOT EXISTS products (        id INTEGER PRIMARY KEY, name TEXT, category TEXT    )''')    cursor.execute("DELETE FROM orders")    cursor.execute("DELETE FROM products")    cursor.execute("INSERT INTO products VALUES (1,'手机','数码'),(2,'电脑','数码'),(3,'衬衫','服装')")    cursor.execute('''    INSERT INTO orders VALUES     (1,1,1,5999,'2025-01-10','华东'),    (2,2,2,7999,'2025-01-15','华北'),    (3,3,3,299,'2025-02-05','华东'),    (4,1,2,7999,'2025-02-20','华北'),    (5,2,1,6999,'2025-03-08','华东'),    (6,3,2,8999,'2025-03-12','华北')    ''')    conn.commit()    conn.close()    logger.info("✅ 数据库初始化完成")# ===================== 数据格式化 =====================defdf_to_struct_data(df: pd.DataFrame) -> dict:    df = df.loc[:, ~df.columns.duplicated()]    data = {"columns": df.columns.tolist(),"rows": df.to_dict(orient="records")    }    logger.info(f"📊 结构化数据:{json.dumps(data, ensure_ascii=False)}")return data# ===================== 生成ECharts HTML(浏览器打开) =====================defgenerate_echarts_html(option: str, question: str) -> str:    html = f'''    <!DOCTYPE html>    <html lang="zh-CN">    <head>        <meta charset="UTF-8">        <title>{question} - ECharts图表</title>        <script src="https://cdn.bootcdn.net/ajax/libs/echarts/5.4.3/echarts.min.js"></script>        <style>            html,body{{height:100%;margin:0;padding:10px;box-sizing:border-box;}}            #chart{{width:100%;height:100%;}}        </style>    </head>    <body>        <div id="chart"></div>        <script>            var chart = echarts.init(document.getElementById('chart'));            var option = {option};            // 全局错误处理            try {{                chart.setOption(option);                window.onresize = function(){{chart.resize();}};            }} catch(e) {{                document.getElementById('chart').innerHTML =                     '<div style="color:red;padding:20px;font-size:16px;">图表渲染失败:' + e.message + '<br>请检查数据格式</div>';                console.error('ECharts渲染错误:', e);            }}        </script>    </body>    </html>    '''with open("echarts_temp.html""w", encoding="utf-8"as f:        f.write(html)    logger.info("✅ ECharts HTML文件生成完成")return"echarts_temp.html"# ===================== NL→DSL(兼容所有LLM返回格式) =====================defnl_to_dsl(nl_query):    logger.info(f"🔍 用户问题:{nl_query}")    prompt = """    输出纯JSON,不要任何解释和markdown格式    数据库表:orders(id, user_id, product_id, amount, create_time, region)、products(id, name, category)    关联关系:orders.product_id = products.id    严格按照以下格式输出:    {"tables":["orders"],"metrics":["聚合函数1","聚合函数2"],"dimensions":["维度1","维度2"],"filters":[{"field":"字段名","op":"操作符","value":"值"}]}    示例:    - 查询各地区订单数量 → {"tables":["orders"],"metrics":["count(id)"],"dimensions":["region"],"filters":[]}    - 查询华东地区总销售额 → {"tables":["orders"],"metrics":["sum(amount)"],"dimensions":[],"filters":[{"field":"region","op":"=","value":"华东"}]}    - 查询2025年销售额趋势 → {"tables":["orders"],"metrics":["sum(amount)"],"dimensions":["create_time"],"filters":[{"field":"create_time","op":">=","value":"2025-01-01"}]}    """try:        res = client.chat.completions.create(            model="qwen-turbo",            messages=[{"role""system""content": prompt}, {"role""user""content": nl_query}],            temperature=0.01,            response_format={"type""json_object"}        )        raw_content = res.choices[0].message.content.strip()        logger.info(f"📝 LLM原始输出:{raw_content}")# 清理markdown格式        raw = re.sub(r'```.*?```''', raw_content, flags=re.DOTALL).strip()        raw = raw.replace("order_date""create_time").replace("订单数量""count(id)").replace("销售额""sum(amount)")        dsl = json.loads(raw)# 终极兼容:处理所有可能的metrics格式        fixed_metrics = []for item in dsl.get("metrics", []):if isinstance(item, str):                fixed_metrics.append(item)elif isinstance(item, dict):for key in ["expression""value""func""field""name"]:if key in item:                        val = item[key]if key == "func"and"field"in item:                            fixed_metrics.append(f"{val}({item['field']})")elif re.match(r'^[a-zA-Z]+\(.*\)$', str(val)):                            fixed_metrics.append(str(val))breakelse:                    logger.warning(f"⚠️ 无法解析的metrics字典:{item},使用默认count(id)")                    fixed_metrics.append("count(id)")        dsl["metrics"] = fixed_metricsif"tables"notin dsl:            dsl["tables"] = ["orders"]        logger.info(f"✅ 最终DSL:{json.dumps(dsl, ensure_ascii=False)}")return dslexcept json.JSONDecodeError as e:        logger.error(f"❌ JSON解析失败:{str(e)},原始内容:{raw_content}")return {"tables": ["orders"], "metrics": ["count(id)"], "dimensions": ["region"], "filters": []}except Exception as e:        logger.error(f"❌ DSL生成失败:{str(e)}", exc_info=True)returnNone# ===================== DSL→SQL(解决字段歧义和过滤器KeyError) =====================defdsl_to_sql(dsl):try:        metrics = dsl.get("metrics", [])        dimensions = dsl.get("dimensions", [])        filters = dsl.get("filters", [])# 给所有字段加上表名前缀,解决ambiguous column错误        fixed_metrics = []for m in metrics:            m = re.sub(r'(\w+)\((\w+)\)'r'\1(orders.\2)', m)            fixed_metrics.append(m)        metrics = fixed_metrics        fixed_dims = []for d in dimensions:if"."notin d:                d = f"orders.{d}"            fixed_dims.append(d)        dimensions = fixed_dims# 明细查询ifnot metrics:            sql = "SELECT orders.*, products.name, products.category FROM orders LEFT JOIN products ON orders.product_id=products.id"# 统计查询else:            select = ", ".join(metrics + dimensions)            where = []# 兼容两种过滤器格式,解决KeyError: 'value'for f in filters:if isinstance(f, dict) and f.get("field"):                    field = f["field"]                    op = None                    value = Noneif"op"in f and"value"in f:                        op = f["op"]                        value = f["value"]else:                        op_map = {"ge"">=""le""<=""gt"">""lt""<","eq""=""ne""!="                        }for key in f:if key != "field":                                op = op_map.get(key, key)                                value = f[key]breakif op and value isnotNone:if"."notin field:                            field = f"orders.{field}"                        where.append(f"{field}{op} '{value}'")            where_clause = f"WHERE {' AND '.join(where)}"if where else""            group = f"GROUP BY {', '.join(dimensions)}"if dimensions else""            sql = f"SELECT {select} FROM orders LEFT JOIN products ON orders.product_id=products.id {where_clause}{group}"        sql = re.sub(r'\s+'' ', sql).strip()        logger.info(f"✅ 生成SQL:{sql}")return sqlexcept Exception as e:        logger.error(f"❌ SQL生成失败:{str(e)}", exc_info=True)returnNone# ===================== 执行SQL =====================defexec_sql(sql):try:        conn = sqlite3.connect("nl2dsl_llm.db")        df = pd.read_sql(sql, conn)        conn.close()        logger.info(f"✅ SQL执行成功,数据行数:{len(df)}")return dfexcept Exception as e:        logger.error(f"❌ SQL执行失败:{str(e)}", exc_info=True)returnNone# ===================== 【核心修复】ECharts配置生成(自动数据绑定+多层兜底) =====================defgenerate_echarts_option(question, data):    logger.info("📊 生成ECharts配置")# 第一步:自动识别维度列和指标列    columns = data["columns"]    rows = data["rows"]    dim_col = None    metric_cols = []# 智能识别维度和指标for col in columns:# 维度列:包含时间、地区、品类、名称等关键词if any(keyword in col.lower() for keyword in ["time""date""region""category""name""area""province"]):            dim_col = col# 指标列:数值类型elif all(isinstance(row.get(col), (int, float)) for row in rows if row.get(col) isnotNone):            metric_cols.append(col)# 如果没有识别到维度列,使用第一列作为维度ifnot dim_col and len(columns) > 0:        dim_col = columns[0]# 如果没有识别到指标列,使用所有数值列ifnot metric_cols:for col in columns:if col != dim_col and all(isinstance(row.get(col), (int, float)) for row in rows if row.get(col) isnotNone):                metric_cols.append(col)# 提取维度值和指标值    dim_values = [row.get(dim_col, ""for row in rows] if dim_col else []    metric_values = {}for col in metric_cols:        metric_values[col] = [row.get(col, 0for row in rows]    logger.info(f"🔍 自动识别:维度列={dim_col}, 指标列={metric_cols}")# 第二步:智能选择图表类型    chart_type = "bar"if dim_col and any(keyword in dim_col.lower() for keyword in ["time""date"]):        chart_type = "line"# 时间维度用折线图elif len(metric_cols) == 1and len(dim_values) <= 6:        chart_type = "pie"# 少量分类用饼图# 第三步:生成基础配置(兜底配置,确保一定能渲染)    base_option = {"title": {"text": question, "left""center"},"tooltip": {"trigger""axis"},"legend": {"top""bottom"},"xAxis": {"type""category""data": dim_values},"yAxis": {"type""value"},"series": []    }# 添加系列数据for metric_name, values in metric_values.items():# 清理指标名称(去掉sum(、count(等前缀)        clean_name = re.sub(r'^[a-zA-Z]+\((.*)\)$'r'\1', metric_name)if clean_name == metric_name:            clean_name = f"指标_{metric_name}"        base_option["series"].append({"name": clean_name,"type": chart_type,"data": values,"smooth": chart_type == "line",  # 折线图开启平滑"label": {"show"True"position""top"}        })# 第四步:尝试使用LLM生成更优的配置try:        prompt = f"""        输出完整的ECharts JSON配置,不要任何解释和markdown格式        必须严格使用以下数据,禁止使用示例数据:        维度值:{json.dumps(dim_values, ensure_ascii=False)}        指标值:{json.dumps(metric_values, ensure_ascii=False)}        图表类型:{chart_type}        要求:        1. series.data必须是纯数值数组,不能是字符串或对象        2. xAxis.data必须是维度值数组        3. 必须包含title、tooltip、legend、xAxis、yAxis、series字段        4. 折线图设置smooth: true        5. 饼图的series.data格式为:[{"value":数值,"name":"名称"},...]        """        res = client.chat.completions.create(            model="qwen-turbo",            messages=[{"role""system""content": prompt}],            temperature=0.01,            response_format={"type""json_object"}        )        raw_content = res.choices[0].message.content.strip()        logger.info(f"📝 LLM生成的ECharts配置:{raw_content}")# 清理并验证LLM生成的配置        opt_str = re.sub(r'```.*?```''', raw_content, flags=re.DOTALL).strip()        llm_option = json.loads(opt_str)# 验证并修复LLM生成的配置if"series"in llm_option and isinstance(llm_option["series"], list):for i, series in enumerate(llm_option["series"]):# 确保series.data是正确的格式if"data"notin series ornot isinstance(series["data"], list):# 使用我们提取的正确数据if i < len(base_option["series"]):                        series["data"] = base_option["series"][i]["data"]else:                        series["data"] = []# 饼图特殊处理if series.get("type") == "pie":# 转换为饼图需要的格式                    pie_data = []for j, val in enumerate(series["data"]):if isinstance(val, (int, float)) and j < len(dim_values):                            pie_data.append({"value": val, "name": dim_values[j]})elif isinstance(val, dict) and"value"in val and"name"in val:                            pie_data.append(val)                    series["data"] = pie_data# 合并基础配置和LLM配置(基础配置兜底)        final_option = {**base_option, **llm_option}        logger.info(f"✅ 最终ECharts配置:{json.dumps(final_option, ensure_ascii=False)}")return json.dumps(final_option, ensure_ascii=False)except Exception as e:        logger.error(f"❌ LLM生成ECharts配置失败,使用兜底配置:{str(e)}", exc_info=True)# 直接使用我们生成的基础配置return json.dumps(base_option, ensure_ascii=False)# ===================== GUI界面 =====================classApp:def__init__(self, root):        self.root = root        self.root.title("自然语言查询 + ECharts可视化")        self.root.geometry("1200x850")        tk.Label(root, text="🚀 自然语言 → SQL → ECharts 图表", font=("微软雅黑"16"bold")).pack(pady=10)# 问题选择        self.questions = ["请选择问题","查询华东地区总销售额","查询各地区订单数量","查询2025年销售额趋势","查询所有订单明细"        ]        self.sel = tk.StringVar()        combo = ttk.Combobox(root, textvariable=self.sel, values=self.questions, width=100, font=("微软雅黑"11))        combo.pack(pady=5)        combo.current(0)        combo.bind("<<ComboboxSelected>>", self.fill_question)# 输入框        self.entry = ttk.Entry(root, width=100, font=("微软雅黑"12))        self.entry.pack(pady=5)        ttk.Button(root, text="🚀 执行查询 + 生成图表", command=self.run, width=30).pack(pady=8)# 展示区域        frame = ttk.Frame(root)        frame.pack(fill=tk.X, padx=10)        tk.Label(frame, text="📦 DSL配置").grid(row=0, column=0)        self.dsl_text = scrolledtext.ScrolledText(frame, height=5, width=55)        self.dsl_text.grid(row=1, column=0, padx=5)        tk.Label(frame, text="🧾 SQL语句").grid(row=0, column=1)        self.sql_text = scrolledtext.ScrolledText(frame, height=5, width=55)        self.sql_text.grid(row=1, column=1, padx=5)        tk.Label(root, text="📊 查询结果").pack()        self.res_text = scrolledtext.ScrolledText(root, height=8, width=130)        self.res_text.pack(pady=5)        tk.Label(root, text="📜 ECharts配置").pack()        self.ech_text = scrolledtext.ScrolledText(root, height=8, width=130)        self.ech_text.pack(pady=5)deffill_question(self, e):        self.entry.delete(0, tk.END)        self.entry.insert(0, self.sel.get())defrun(self):        q = self.entry.get().strip()ifnot q or q == self.questions[0]:            messagebox.showwarning("提示""请选择或输入问题")return# 清空展示区        self.dsl_text.delete(1.0, tk.END)        self.sql_text.delete(1.0, tk.END)        self.res_text.delete(1.0, tk.END)        self.ech_text.delete(1.0, tk.END)# 1. 生成DSL        dsl = nl_to_dsl(q)ifnot dsl:            messagebox.showerror("错误""DSL生成失败,请查看日志")return        self.dsl_text.insert(tk.END, json.dumps(dsl, indent=2, ensure_ascii=False))# 2. 生成SQL        sql = dsl_to_sql(dsl)ifnot sql:            messagebox.showerror("错误""SQL生成失败,请查看日志")return        self.sql_text.insert(tk.END, sql)# 3. 执行SQL        df = exec_sql(sql)if df isNoneor df.empty:            self.res_text.insert(tk.END, "查询结果为空或执行失败")            messagebox.showwarning("提示""查询结果为空")return        self.res_text.insert(tk.END, df.to_string(index=False))# 4. 生成ECharts配置        struct_data = df_to_struct_data(df)        ech_opt = generate_echarts_option(q, struct_data)        self.ech_text.insert(tk.END, ech_opt)# 5. 浏览器打开图表try:            html_file = generate_echarts_html(ech_opt, q)            webbrowser.open(os.path.abspath(html_file))            messagebox.showinfo("成功""✅ 图表已在浏览器打开!")except Exception as e:            logger.error(f"❌ 打开浏览器失败:{str(e)}")            messagebox.showerror("错误""图表生成成功,但打开浏览器失败")# ===================== 启动 =====================if __name__ == "__main__":    init_test_db()    root = tk.Tk()    App(root)    root.mainloop()

主要修复点说明

  1. 自动数据绑定逻辑

    • 自动识别维度列(包含time、date、region、category等关键词)
    • 自动识别指标列(数值类型的列)
    • 提取正确的维度值和指标值数组
  2. 多层级兜底机制

    • 第一层:LLM生成优化配置
    • 第二层:自动生成基础配置(确保100%能渲染)
    • 第三层:HTML中添加全局错误处理,避免页面崩溃
  3. 图表类型智能选择

    • 时间维度 → 折线图(自动开启smooth)
    • 地区/品类维度 → 柱状图
    • 少量分类(≤6个) → 饼图
  4. 数据格式强制验证

    • 确保series.data始终是数值数组
    • 饼图自动转换为[{"value":数值,"name":"名称"}]格式
    • 清理指标名称中的函数前缀(如sum(amount)amount
  5. 增强的错误处理

    • HTML中添加try-catch捕获ECharts渲染错误
    • 页面显示友好的错误提示
    • 控制台输出详细错误信息

最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-07-03 03:47:30 HTTP/2.0 GET : https://f.mffb.com.cn/a/496004.html
  2. 运行时间 : 0.207180s [ 吞吐率:4.83req/s ] 内存消耗:4,432.20kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=ccefed0b5c12ff4e3b94e8d8deecd3d2
  1. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_static.php ( 4.90 KB )
  7. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  10. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  11. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  12. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  13. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  14. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  15. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  16. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  17. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  18. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  19. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  21. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  22. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/provider.php ( 0.19 KB )
  23. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  24. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  25. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  26. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/common.php ( 0.03 KB )
  27. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  28. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  29. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/app.php ( 0.95 KB )
  30. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cache.php ( 0.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/console.php ( 0.23 KB )
  32. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cookie.php ( 0.56 KB )
  33. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/database.php ( 2.48 KB )
  34. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  35. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/filesystem.php ( 0.61 KB )
  36. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/lang.php ( 0.91 KB )
  37. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/log.php ( 1.35 KB )
  38. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/middleware.php ( 0.19 KB )
  39. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/route.php ( 1.89 KB )
  40. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/session.php ( 0.57 KB )
  41. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/trace.php ( 0.34 KB )
  42. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/view.php ( 0.82 KB )
  43. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/event.php ( 0.25 KB )
  44. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  45. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/service.php ( 0.13 KB )
  46. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/AppService.php ( 0.26 KB )
  47. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  48. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  49. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  50. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  51. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  52. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/services.php ( 0.14 KB )
  53. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  54. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  55. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  56. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  57. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  58. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  59. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  60. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  61. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  62. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  63. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  64. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  65. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  66. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  67. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  68. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  69. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  70. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  71. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  72. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  73. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  74. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  75. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  76. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  77. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  78. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  79. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  80. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  81. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  82. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  83. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/Request.php ( 0.09 KB )
  84. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  85. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/middleware.php ( 0.25 KB )
  86. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  87. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  88. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  89. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  90. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  91. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  92. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  93. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  94. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  95. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  96. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  97. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  98. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  99. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/route/app.php ( 1.72 KB )
  100. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  101. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  102. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  103. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/controller/Index.php ( 4.81 KB )
  104. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/BaseController.php ( 2.05 KB )
  105. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  106. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  108. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  109. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  110. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  111. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  112. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  113. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  114. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  115. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  116. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  117. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  118. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  119. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  120. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  121. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  122. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  123. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  124. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  125. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  126. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  127. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  128. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  129. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  130. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  131. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  132. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  133. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  134. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  135. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  136. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  137. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  138. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  139. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/runtime/temp/067d451b9a0c665040f3f1bdd3293d68.php ( 11.98 KB )
  140. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.001279s ] mysql:host=127.0.0.1;port=3306;dbname=f_mffb;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.001714s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.000741s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.000692s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.001339s ]
  6. SELECT * FROM `set` [ RunTime:0.000618s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.001443s ]
  8. SELECT * FROM `article` WHERE `id` = 496004 LIMIT 1 [ RunTime:0.006664s ]
  9. UPDATE `article` SET `lasttime` = 1783021650 WHERE `id` = 496004 [ RunTime:0.003083s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 66 LIMIT 1 [ RunTime:0.000674s ]
  11. SELECT * FROM `article` WHERE `id` < 496004 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.001287s ]
  12. SELECT * FROM `article` WHERE `id` > 496004 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.001286s ]
  13. SELECT * FROM `article` WHERE `id` < 496004 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.007073s ]
  14. SELECT * FROM `article` WHERE `id` < 496004 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.011331s ]
  15. SELECT * FROM `article` WHERE `id` < 496004 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.015566s ]
0.211009s