ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

项目改造日记:模板插件机制

2026/9/6 14:36:26 拓冰建站 浏览量
项目改造日记:模板插件机制 模板插件机制使用文档适用版本单体整合版PySide6 / Python 3.8对应模块core/pdf_router.py、plugins/本文档说明如何为一种新的 PDF 样式开发一个模板插件。目标新增 PDF 模板时无需改动核心代码只需在plugins/下放置一个.py文件。1. 机制概述单体版曾提供两条生成标准模板的路径路径原理扩展性手工【转换】从按页识别结果_pages.xlsx提取并转 1 表格式通用、不依赖插件模板插件解析本机制按模板判据路由到专属插件直接解析 PDF → 标准格式新增样式只需加插件插件机制由三部分组成插件注册表core/pdf_router.py::load_plugins()启动时扫描plugins/下所有.py模块。路由分派route_pdf(pdf_path)依判据遍历插件命中则调用其parse()。兜底策略 a判据命中但解析失败时立即报错并提示疑似模板由人工介入不自动降级、不静默吞错所有插件均未命中时抛出TemplateNotMatched。2. 目录与文件源码/ ├── core/ │ └── pdf_router.py # 路由引擎勿改除非升级机制本身 ├── plugins/ │ ├── __init__.py │ ├── 1001_style.py # 示例模板KM Aluminium COA / HPA 批次 │ └── 你新增的样式.py # 每个模板一个文件 └── temp_folder/ # 输出目录{母锭号}_std.xlsx新增模板 在plugins/放一个新.py文件下次启动或再次加载时自动被发现。3. 插件协议每个插件必须暴露 4 个接口load_plugins()会校验缺失并跳过不完整插件属性/方法类型必需说明TEMPLATE_IDstr否模板标识用于错误提示与日志缺省用文件名PRIORITYint否优先级数字小优先缺省999。用于两个插件判据可能同时命中时排序recognizes(pdf_path) - bool函数是自报判据判断本插件能否识别这份 PDFparse(pdf_path) - list[dict]函数是专属解析把 PDF 解析成标准记录列表PRIORITY判定多个插件判据都命中时PRIORITY小者先尝试。建议给特征专一、结构明确的模板设更小的值。返回结构标准记录parse()必须返回list[dict]每个dict是一行标准记录键如下键含义是否必需备注ParentIngotSn母锭号 / 批次号是用于分组生成结果文件ElementName元素名称是如Si、FeResultValue结果值是数值去除分隔符可float或strSign符号 / 运算值否如、≤ 0.01应拆为Sign、ResultValue0.01Remark备注否可为Noneroute_pdf会按ParentIngotSn分组每组生成一个{母锭号}_std.xlsx并把源 PDF 文件名自动写入「File」列。因此parse()无需自行写文件。4. 示例1001_style.py已实现importreimportpdfplumber TEMPLATE_ID1001# 供错误提示/日志PRIORITY1# 数字小优先defrecognizes(pdf_path):判据首页含证书与供应商关键字。withpdfplumber.open(pdf_path)aspdf:firstpdf.pages[0].extract_text()orreturn(Certificate of analysisinfirst)and(KM Aluminiuminfirst)defparse(pdf_path):解析逐页取批次号与元素成分映射为标准记录。records[]withpdfplumber.open(pdf_path)aspdf:forpageinpdf.pages:tablespage.extract_tables()batch_extract_batch(tables)# 取 Lot No.forelem,num,signin_tables_for_page(page):records.append({ParentIngotSn:batch,ElementName:elem,ResultValue:num,Sign:sign,Remark:None,})returnrecords5. 如何新增一个模板推荐流程明确判据强特征选该样式 PDF 独有、稳定的特征如首页固定标题、固定供应商名、固定生产企业名。避免用易与大类混淆的弱特征如仅含 Composition。确认版面解析建议先用pdfplumber跑extract_tables()/extract_text()观察版面确认元素能稳定提取、列能对齐。编写插件在plugins/新建名称.py实现recognizes与parse可参考1001_style.py。自测用python -c单次调用route_pdf见第 6 节。隔离健壮性parse()内部做好异常处理与字段兜底避免个别页面异常导致整份失败。6. 调试与调用6.1 Python 脚本调用f:\ECoaService\环境\venv\Scripts\python.exe-c import sys; sys.path.insert(0, rf:\ECoaService\源码) from core.pdf_router import route_pdf, load_plugins plugs load_plugins() # 查看已加载插件 print([(p.template_id, p.priority) for p in plugs]) print(route_pdf(r你的.pdf 绝对路径, output_dirNone, pluginsplugs)) output_dirNone时默认输出到temp_folder/。6.2 在客户端打开「本机直接处理」页选择 PDF点击「解析为标准模板」按钮底部会预览首个结果文件、状态栏显示生成数量、日志区列出每个{母锭号}_std.xlsx完整路径。6.3 常见提示提示含义处理TemplateNotMatched所有插件判据均未命中核对 PDF 是否符合已注册模板若是新样式新增插件PluginParseError疑似模板 [ID]…判据命中但parse()抛异常兜底策略 a需人工介入检查parse()对该 PDF 的版面假设7. 开发规范与注意判据务必先强且唯一多个模板特征相近时加PRIORITY拉开顺序避免误判。单点失败隔离parse()建议对每页/每项做异常兜底一个坏插件不会拖垮引擎load_plugins会对加载异常隔离。性能recognizes()通常只读首页parse()全量读取是正常开销但避免在插件内做无关重活。统一 schema所有插件最终都映射到 1.xlsx 的标准列不要各写各的列否则下游无法统一。不改pdf_router.py除非升级机制本身日常新增模板只动plugins/。8. 与既有功能的关系插件解析PDF 直出标准模板与手工【识别 → 转换】目标一致但路径不同可并存长期建议以插件解析为主。插件解析结果可直接复用现有 include json 提取/取数逻辑数据驱动规则两者不冲突。