当前位置:首页>python>Python + Win32COM 自动化重构:从资产清查看电子签名批量注入的架构实践

Python + Win32COM 自动化重构:从资产清查看电子签名批量注入的架构实践

  • 2026-08-22 13:13:19
Python + Win32COM 自动化重构:从资产清查看电子签名批量注入的架构实践

直接运行版本

一、 资产报废潮下的效率危机:从行政噩梦到技术破局

在资产管理的生命周期,『固定资产报废与处置』合规要求高、流程繁琐。无论服务器、生产流水线机械设备,还是办公终端,一旦进入清理报废程序,财务、法务与 IT 部门就要对每项资产进行核销、变卖或清理,并附上具备法律效力的批复与电子签名。
上周,因办公地点搬迁与技术迭代,多台机要一起报废变卖。按合规要求,每份报废明细表、每栏流转记录,都必须由相关部门负责人落款签名。
然而,部门面临的现实却是:
  1. 人工粘贴效率低:数十份表格、过百个待签署单元格,全靠行政人员手动把签名图片逐张拖进 Excel、拉伸变形、对齐网格。
  2. 格式破坏严重:传统手动插入图片极易导致图像拉伸变形,或者在筛选、排序表格时发生图片移位、重叠,使得最终打印或归档的 PDF 校验失败。
  3. 版本管控混乱:缺乏标准化工具,员工自行使用不同的 Excel 插件,导致部分文件在跨平台传输时丢失关联图片。
此典型的效率卡点,靠加班「硬扛」不仅累,且易人为错误。所以将此类重复性高、逻辑明确的行政噩梦,转化为自动化、零差错的程序流程。
本文将基于 Python 开发的『Excel 空白单元格批量签名注入工具』为例,深入剖析如何用GUI 工厂模式(Registry Factory Pattern)Windows COM 组件自动化(Win32COM Interop),打造兼具高扩展性、高鲁棒性与像素级排版精度的效率工具。


二、 业务需求拆解与技术选型分析

先从业务逻辑与技术架构两个维度完成需求细化拆解。

1. 业务痛点与技术映射

业务需求技术挑战解决方案
精准识别空白签名栏普通 Excel 库对复杂样式或合并单元格的识别存在偏差使用底层 COM 组件逐行扫描 UsedRange,精确判断单元格 Value 状态
签名图片保持比例对齐图像直接拉伸会导致签名失真,无法满足合规要求开启 LockAspectRatio 锁比例,计算单元格坐标并自动反向撑大行高
表格调整时图片不移位排序或插入行时图片位置漂移将 Shape 的 Placement 属性严格设置为 1(跟随单元格移动与改变大小)
操作界面需傻瓜化行政人员不具备命令行使用能力,且需要动态选择表格 Sheet基于 Tkinter 封装桌面 GUI,支持文件拖拽选择与 Sheet 动态预读
组件代码易维护易扩充UI 界面代码极易写成「意大利面条」式的耦合结构引入 数据类(DataClass) 与 注册式工厂模式(Registry Factory)

2. 为何是 Win32COM 而非 OpenPyXL 或 Pandas?

Python 处理 Excel 常用 pandasopenpyxl 与 xlsxwriter。本场景特选 win32com.client(即 Windows COM 自动化接口)。因为:
  1. openpyxl / pandas 的局限:处理纯文本和数值数据表现卓越,但对 Excel 内部复杂的图形对象(Shapes/Pictures)的定、动态行高自适应计算,和调用 Excel 原生排版引擎支持较弱。
  2. Win32COM 的绝对优势:直接调用 Windows 的 Excel.Application COM 服务,相当于隐藏的 Excel 实例在后台操作。不仅完美继承 Excel 的所有排版能力,还实时计算单元格在像素(Points)层面的 LeftTopWidth 和 Height,确保图片插入效果与人工精准拖拽一致。


三、 架构设计:为何选择「注册式工厂模式」构建 GUI?

GUI开发常见的坑就是将界面布局代码、控件样式定义与业务逻辑深度绑定。一旦界面控件增多,代码随之膨胀得难以维护。
为解决此问题,本项目引入注册式工厂模式(Registry Widget Factory),配合 Python 的 @dataclass 统一配置管理。

1. 统一配置对象:UIConfig

通过 Python 3.7+ 引入的 dataclass,将所有 UI 控件的字体、边距、颜色、宽度等硬编码参数解耦收敛。若他朝改变视觉 UI 标准,只修改该配置类,无需改动任何控件生成逻辑。
from dataclasses import dataclass# ================= 1. 统一配置对象 =================@dataclassclass UIConfig:    font_normal: tuple = ("Microsoft YaHei UI"10)    font_title: tuple = ("Microsoft YaHei UI"12"bold")    entry_width: int = 35    combo_width: int = 20    padx: int = 8    pady: int = 6    button_pady: int = 10    label_fg: str = "#222222"

2. 工厂解耦与组合控件注册

传统 Tkinter 创建每个控件都重复 font=...padx=...grid(...) 等大量冗余参数。在本项目,将基础控件(Label, Entry, Button, Combobox)与业务组合控件(File Picker)统一抽离为独立函数,并以 RegistryWidgetFactory 集中管理:
# ================= 2. 基础控件创建函数 =================def create_label(factory, parent, text, row, column=0, columnspan=1, **kwargs):    options = {        "text": text,        "font": factory.config.font_normal,        "fg": factory.config.label_fg    }    options.update(kwargs)    widget = tk.Label(parent, **options)    widget.grid(row=row, column=column, columnspan=columnspan, padx=factory.config.padx, pady=factory.config.pady, sticky="w")    return factory.remember(widget)def create_entry(factory, parent, row, column=1, columnspan=1, textvariable=None, width=None, **kwargs):    options = {        "font": factory.config.font_normal,        "width": width or factory.config.entry_width,        "textvariable": textvariable    }    options.update(kwargs)    widget = tk.Entry(parent, **options)    widget.grid(row=row, column=column, columnspan=columnspan, padx=factory.config.padx, pady=factory.config.pady, sticky="ew")    return factory.remember(widget)def create_button(factory, parent, text, command, row, column=0, columnspan=1, **kwargs):    # 支持自定义样式(如背景色)时使用 tk.Button,否则使用默认 ttk.Button    if "bg" in kwargs or "fg" in kwargs:        widget = tk.Button(parent, text=text, command=command, **kwargs)    else:        widget = ttk.Button(parent, text=text, command=command)    widget.grid(row=row, column=column, columnspan=columnspan, padx=factory.config.padx, pady=factory.config.button_pady, sticky="ew")    return factory.remember(widget)def create_combobox(factory, parent, row, column=1, columnspan=1, values=None, textvariable=None, width=None, **kwargs):    widget = ttk.Combobox(        parent,        values=values or [],        textvariable=textvariable,        width=width or factory.config.combo_width,        state="readonly"    )    widget.grid(row=row, column=column, columnspan=columnspan, padx=factory.config.padx, pady=factory.config.pady, sticky="ew")    return factory.remember(widget)# ================= 3. 注册式自定义组合控件 =================def create_file_picker(factory, parent, label_text, row, variable, command):    """组合控件:标签 + 输入框 + 浏览按钮"""    # 占用整行显示提示标签    factory.create("label", parent, text=label_text, row=row, column=0, columnspan=3)    # 输入框和按钮放在下一行    entry = factory.create("entry", parent, row=row+1, column=0, columnspan=2, textvariable=variable)    factory.create("button", parent, text="浏览...", command=command, row=row+1, column=2)    return entry

3. 核心工厂类实现:RegistryWidgetFactory

核心工厂类维持一个 _creators 映射字典。系统初始化时注册默认控件;界面要生成控件时,调用 factory.create("type", ...) 即可实例化与自动布局。该设计赋予程序扩展能力:若加入全新的富文本控件或自定义图表控件,只需注入新的创建函数,无需修改原有的 UI 结构代码。
# ================= 4. 核心工厂类 =================class RegistryWidgetFactory:    """注册式 Tkinter 控件工厂"""    def __init__(self, config: UIConfig):        self.config = config        self._creators = {}        self._widgets = []        self.register_defaults()    def register(self, widget_type, creator):        if not callable(creator):            raise TypeError("creator 必须是可调用对象")        self._creators[widget_type] = creator    def create(self, widget_type, parent, **kwargs):        creator = self._creators.get(widget_type)        if creator is None:            available = ", ".join(sorted(self._creators.keys()))            raise KeyError(f"未注册控件类型: {widget_type},可用类型: {available}")        return creator(factory=self, parent=parent, **kwargs)    def remember(self, widget):        self._widgets.append(widget)        return widget    def register_defaults(self):        self.register("label", create_label)        self.register("entry", create_entry)        self.register("button", create_button)        self.register("combobox", create_combobox)        self.register("file_picker", create_file_picker)
架构亮点解析:
通过该模式,主界面类ExcelImageInsertApp 的 UI 构建逻辑(build_ui)变整洁。用声明式的视角快速搭建界面,将精力集中在后端的业务自动化流程。

四、 核心攻坚:基于 Python Win32COM 的像素级图形排版

自动化注入电子签名的核心难题在于:Excel 单元格原有排版不遭破坏的前提下,如何实现图片的自适应缩放与居中放置?
Win32COM 的图片以 Shape 对象的形式存在工作表(Worksheet)。直接插入图片不仅不感知单元格的大小,还会按照图片的原始像素尺寸遮盖其他内容。
为了像素级的精准匹配,有以下几项关键技术:

1. 坐标与 MarginsPadding 边距计算

将图片放入单元格,若刚好贴合单元格边缘,会导致视觉极其压抑,打印容易压线。因此,加入 2pt 的内边距(Padding):
left, top = cell.Left + 2, cell.Top + 2

2. 比例锁定与宽度适配

为防止签名图片拉伸变形,必须锁定长宽比,先将图片的宽度调整为单元格宽度减去内边距(cell.Width - 4):
shp = ws.Shapes.AddPicture(img_path, FalseTrue, left, top, -1, -1)shp.LockAspectRatio = Trueshp.Width = cell.Width - 4

3. 反向自适应撑大行高(RowHeight Autocount)

宽度适配后,图片的高度(shp.Height)根据原始长宽比自动计算。此时,若单元格的高度低于图片高度,图片就溢到下一行。
因此,代码引入动态行高自适应算法:判断当前行高是否小于 shp.Height + 4,若是,则自动拉伸该行的 RowHeight,确保图片完全被包在单元格内。
if ws.Rows(i).RowHeight < shp.Height + 4:    ws.Rows(i).RowHeight = shp.Height + 4

4. 关键的 DOM 属性:Placement = 1

确保自动生成的文件使用时「不崩塌」的关键。Excel的Shape 对象的 Placement 属性定义对象与单元格的物理绑定关系:
  1. Placement = 1 (xlMoveAndSize):图片随单元格移动并改变大小。
  2. Placement = 2 (xlMove):图片随单元格移动,但不改变大小。
  3. Placement = 3 (xlFreeFloating):图片完全自由浮动。
强制指定 shp.Placement = 1,即使后续业务人员对 Excel 筛选、插入新行或调整列宽,电子签名也会始终固定在原本的单元格,绝不乱飘。


五、 全流程源码解析与业务逻辑闭环

以下是完整的代码。代码涵盖 UI 界面初始化、Sheet 名称异步预读、批量空值检测、签名注入以及最后自动开启 Excel 查看结果的完整业务闭环。
import tkinter as tkfrom tkinter import ttk, filedialog, messageboxfrom dataclasses import dataclassimport osimport win32com.client as win32# ================= 1. 统一配置对象 =================@dataclassclass UIConfig:    font_normal: tuple = ("Microsoft YaHei UI"10)    font_title: tuple = ("Microsoft YaHei UI"12"bold")    entry_width: int = 35    combo_width: int = 20    padx: int = 8    pady: int = 6    button_pady: int = 10    label_fg: str = "#222222"# ================= 2. 基础控件创建函数 =================def create_label(factory, parent, text, row, column=0, columnspan=1, **kwargs):    options = {        "text": text,        "font": factory.config.font_normal,        "fg": factory.config.label_fg    }    options.update(kwargs)    widget = tk.Label(parent, **options)    widget.grid(row=row, column=column, columnspan=columnspan, padx=factory.config.padx, pady=factory.config.pady, sticky="w")    return factory.remember(widget)def create_entry(factory, parent, row, column=1, columnspan=1, textvariable=None, width=None, **kwargs):    options = {        "font": factory.config.font_normal,        "width": width or factory.config.entry_width,        "textvariable": textvariable    }    options.update(kwargs)    widget = tk.Entry(parent, **options)    widget.grid(row=row, column=column, columnspan=columnspan, padx=factory.config.padx, pady=factory.config.pady, sticky="ew")    return factory.remember(widget)def create_button(factory, parent, text, command, row, column=0, columnspan=1, **kwargs):    # 支持自定义样式(如背景色)时使用 tk.Button,否则使用默认 ttk.Button    if "bg" in kwargs or "fg" in kwargs:        widget = tk.Button(parent, text=text, command=command, **kwargs)    else:        widget = ttk.Button(parent, text=text, command=command)    widget.grid(row=row, column=column, columnspan=columnspan, padx=factory.config.padx, pady=factory.config.button_pady, sticky="ew")    return factory.remember(widget)def create_combobox(factory, parent, row, column=1, columnspan=1, values=None, textvariable=None, width=None, **kwargs):    widget = ttk.Combobox(        parent,        values=values or [],        textvariable=textvariable,        width=width or factory.config.combo_width,        state="readonly"    )    widget.grid(row=row, column=column, columnspan=columnspan, padx=factory.config.padx, pady=factory.config.pady, sticky="ew")    return factory.remember(widget)# ================= 3. 注册式自定义组合控件 =================def create_file_picker(factory, parent, label_text, row, variable, command):    """组合控件:标签 + 输入框 + 浏览按钮"""    # 占用整行显示提示标签    factory.create("label", parent, text=label_text, row=row, column=0, columnspan=3)    # 输入框和按钮放在下一行    entry = factory.create("entry", parent, row=row+1, column=0, columnspan=2, textvariable=variable)    factory.create("button", parent, text="浏览...", command=command, row=row+1, column=2)    return entry# ================= 4. 核心工厂类 =================class RegistryWidgetFactory:    """注册式 Tkinter 控件工厂"""    def __init__(self, config: UIConfig):        self.config = config        self._creators = {}        self._widgets = []        self.register_defaults()    def register(self, widget_type, creator):        if not callable(creator):            raise TypeError("creator 必须是可调用对象")        self._creators[widget_type] = creator    def create(self, widget_type, parent, **kwargs):        creator = self._creators.get(widget_type)        if creator is None:            available = ", ".join(sorted(self._creators.keys()))            raise KeyError(f"未注册控件类型: {widget_type},可用类型: {available}")        return creator(factory=self, parent=parent, **kwargs)    def remember(self, widget):        self._widgets.append(widget)        return widget    def register_defaults(self):        self.register("label", create_label)        self.register("entry", create_entry)        self.register("button", create_button)        self.register("combobox", create_combobox)        self.register("file_picker", create_file_picker)# ================= 5. 应用主类 (封装业务逻辑) =================class ExcelImageInsertApp:    def __init__(self, root):        self.root = root        self.root.title("Excel 空白单元格批量插图工具")        # 适应 Grid 布局,适当调大窗口        self.root.geometry("480x420+600+300")        self.root.resizable(FalseFalse)        # 初始化工厂        self.factory = RegistryWidgetFactory(UIConfig())        # 绑定变量        self.img_var = tk.StringVar()        self.excel_var = tk.StringVar()        self.sheet_var = tk.StringVar()        self.col_var = tk.StringVar(value="C")        # 预留控件引用        self.sheet_cb = None        self.btn_run = None        self.build_ui()    def build_ui(self):        """完全使用工厂模式和 Grid 布局构建界面"""        form = ttk.Frame(self.root)        form.pack(fill="both", expand=True, padx=20, pady=10)        form.columnconfigure(1, weight=1)        # 1. & 2. 使用我们刚注册的 file_picker 组合控件        self.factory.create("file_picker", form, label_text="1. 选择签名图片:", row=0                            variable=self.img_var, command=self.select_image)        self.factory.create("file_picker", form, label_text="2. 选择Excel文件:", row=2                            variable=self.excel_var, command=self.select_excel)        # 3. 工作表选择        self.factory.create("label", form, text="3. 选择要处理的 Sheet:", row=4, column=0)        self.sheet_cb = self.factory.create("combobox", form, row=4, column=1, textvariable=self.sheet_var)        self.factory.create("label", form, text="(选完Excel后自动读取)", row=4, column=2, fg="gray", font=("Arial"8))        # 4. 插入列选择        self.factory.create("label", form, text="4. 需要填补图片的列:", row=5, column=0)        self.factory.create("entry", form, row=5, column=1, textvariable=self.col_var, width=10, justify="center")        # 5. 执行按钮 (使用自定义样式)        self.btn_run = self.factory.create(            "button", form, text="开始批量插入", command=self.run_process,             row=6, column=0, columnspan=3            bg="#4CAF50", fg="white", font=("Microsoft YaHei"10"bold"), height=2        )    # ----------------- 业务逻辑方法 -----------------    def select_image(self):        path = filedialog.askopenfilename(            title="选择签名图片"            filetypes=[("图片文件""*.png;*.jpg;*.jpeg;*.bmp")]        )        if path:            self.img_var.set(path)    def select_excel(self):        path = filedialog.askopenfilename(            title="选择Excel文件"            filetypes=[("Excel文件""*.xlsx;*.xls;*.xlsm")]        )        if path:            self.excel_var.set(path)            self.load_sheet_names(path)    def load_sheet_names(self, excel_path):        """打开后台 Excel 进程预读所有 Sheet 名称并填充下拉菜单"""        try:            self.btn_run.config(state="disabled"            self.sheet_cb['values'] = ["正在读取..."]            self.sheet_cb.current(0)            self.root.update()            excel = win32.Dispatch('Excel.Application')            excel.Visible = False            excel.DisplayAlerts = False            safe_excel_path = os.path.normpath(excel_path)            wb = excel.Workbooks.Open(safe_excel_path, ReadOnly=True)            sheet_names = [sheet.Name for sheet in wb.Sheets]            wb.Close(SaveChanges=False)            excel.Quit()            self.sheet_cb['values'] = sheet_names            if sheet_names:                self.sheet_cb.current(0        except Exception as e:            messagebox.showwarning("读取Sheet失败"f"无法读取工作表名称,请确认文件未被占用。\n{e}")            self.sheet_cb['values'] = []            self.sheet_var.set("")        finally:            self.btn_run.config(state="normal")    def run_process(self):        """核心业务逻辑:扫描指定列空白格并注入签名"""        img_path = os.path.normpath(self.img_var.get().strip())        exc_path = os.path.normpath(self.excel_var.get().strip())        target_sheet_name = self.sheet_var.get()        col = self.col_var.get().strip().upper()        # 入参校验        if not os.path.exists(img_path):            return messagebox.showwarning("警告""请先选择有效的签名图片!")        if not os.path.exists(exc_path):            return messagebox.showwarning("警告""请先选择有效的Excel文件!")        if not target_sheet_name:            return messagebox.showwarning("警告""请选择需要处理的工作表(Sheet)!")        if not col.isalpha():            return messagebox.showwarning("警告""请输入正确的列字母(例如 C)!")        self.btn_run.config(state="disabled", text="正在处理中...")        self.root.update()        excel = None        wb = None        success_flag = False          inserted_count = 0        try:            excel = win32.Dispatch('Excel.Application')            excel.Visible = False            excel.DisplayAlerts = False            wb = excel.Workbooks.Open(exc_path)            ws = wb.Sheets(target_sheet_name)            # 获取当前 Sheet 使用区域的最大行号            last_row = ws.UsedRange.Rows.Count + ws.UsedRange.Row - 1            for i in range(1, last_row + 1):                cell = ws.Range(f"{col}{i}")                # 仅在单元格为空时插入图片                if cell.Value is None or str(cell.Value).strip() == "":                    left, top = cell.Left + 2, cell.Top + 2                    # 插入并缩放图片逻辑                    shp = ws.Shapes.AddPicture(img_path, FalseTrue, left, top, -1, -1)                    shp.LockAspectRatio = True                    shp.Width = cell.Width - 4                    if ws.Rows(i).RowHeight < shp.Height + 4:                        ws.Rows(i).RowHeight = shp.Height + 4                    shp.Placement = 1                    inserted_count += 1            wb.Save()            success_flag = True          except Exception as e:            messagebox.showerror("错误"f"处理过程中发生错误:\n{str(e)}")        finally:            # 确保 COM 资源彻底释放,防止后台残留 excel.exe 进程            if wb:                try: wb.Close(SaveChanges=False)                exceptpass            if excel:                try: excel.Quit()                exceptpass            self.btn_run.config(state="normal", text="开始批量插入")            if success_flag:                messagebox.showinfo("完成"f"处理成功!图片已维持原比例插入。\n共插入了 {inserted_count} 张签名。\n\n点击「确定」将自动为您打开文件。")                try:                    os.startfile(exc_path)                except Exception as open_err:                    messagebox.showerror("打开文件失败"f"自动打开失败:\n{open_err}")if __name__ == "__main__":    root = tk.Tk()    app = ExcelImageInsertApp(root)    root.mainloop()
已关注
关注
重播 分享


六、 回报分析

评估一项自动化改造引入的标准,是 ROI(Return on Investment,投资回报率)。

1. 效率对比与成本量化

以处理一次500 项报废设备清单(需要插入 500 次电子签名)为例:
评估维度人工手动处理Python 自动化工具处理
平均耗时

约 3 分钟/张(含选择、缩放、对齐)

总计:1500 分钟(25 小时)

整体扫描与处理仅需

12 秒

错误率约 5%(包含拉伸变形、错行、遮挡文本)0%(基于代码规则的绝对对齐)
人力成本消耗行政人员 3 个工作日消耗人力时间:仅需点击一次按钮
合规风控容易因格式错乱被审计部门退回重签完全标准化,符合电子归档审计规范
直接经济效益:仅单次资产清理活动,即可为企业直接节省约 3 个人天的有效工时。若推行至全公司合同归档、财务报销单据核销等场景,年化节省工时可达数百小时。


七、 总结与展望

该代码不仅解决资产报废变卖过程中的行政合规难题,更提供一种可复用的 Python 工具开发范式:
  1.  dataclass 收敛配置,降低维护成本。
  2.  Registry Factory 解耦 UI 控件构建,保障代码可读与可扩充。
  3.  Win32COM 穿透底层办公套件,实现常规 Python 库达不到的像素级掌控力。
该架构可进一步扩展为支持多列并发签名水纹防伪签章注入,甚至与企业内部OA/ERP 的 API无缝对接的智能办公助手。
技术赋能业务,效率改变未来。
#技术分享#Python自动化#Win32COM#工厂模式#办公效率#企业级架构#Excel自动化#软件工程

最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-08-22 18:20:44 HTTP/2.0 GET : https://f.mffb.com.cn/a/511823.html
  2. 运行时间 : 0.275127s [ 吞吐率:3.63req/s ] 内存消耗:4,605.32kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=395a20e8d998bc27933c6df18796730d
  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.000643s ] mysql:host=127.0.0.1;port=3306;dbname=f_mffb;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.001108s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.003083s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.000619s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.001173s ]
  6. SELECT * FROM `set` [ RunTime:0.000471s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.001039s ]
  8. SELECT * FROM `article` WHERE `id` = 511823 LIMIT 1 [ RunTime:0.003851s ]
  9. UPDATE `article` SET `lasttime` = 1787394045 WHERE `id` = 511823 [ RunTime:0.019322s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 66 LIMIT 1 [ RunTime:0.000611s ]
  11. SELECT * FROM `article` WHERE `id` < 511823 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.002147s ]
  12. SELECT * FROM `article` WHERE `id` > 511823 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.026073s ]
  13. SELECT * FROM `article` WHERE `id` < 511823 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.005632s ]
  14. SELECT * FROM `article` WHERE `id` < 511823 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.021254s ]
  15. SELECT * FROM `article` WHERE `id` < 511823 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.015657s ]
0.278043s