CertFlow 系统架构设计文档¶
版本:v2.0 | 最后更新:2026-06-17 | 状态:✅ 与项目结构同步
📋 目录¶
一、概述¶
1.1 文档目的¶
本文档描述 CertFlow 系统的整体架构设计,包括技术栈、分层架构、核心数据流、配置管理、数据库设计及扩展机制,为开发、运维和后续迭代提供指导。
1.2 系统定位¶
CertFlow 是一款桌面应用程序,用于销售计划管理、合格证打印、报告生成和数据查询,替代原有 VBA/Access 方案,提供更稳定、高效的工业品质量文档管理解决方案。
1.3 设计目标¶
目标 |
说明 |
|---|---|
可靠性 |
事务管理、数据一致性、打印稳定性 |
可维护性 |
分层架构、清晰职责、低耦合 |
可扩展性 |
插件化设计、配置驱动、扩展点 |
性能 |
批量操作、异步处理、缓存机制 |
兼容性 |
兼容 Access 数据库、Excel 样式保留 |
二、整体架构概览¶
2.1 架构分层图¶
flowchart TB
subgraph 入口层["入口层"]
GUI["GUI入口<br>main.py"]
CLI["CLI入口<br>cli/__main__.py"]
end
subgraph 表现层["表现层 (Views)"]
VW["views/<br>主窗口/对话框"]
WG["widgets/<br>共享UI组件"]
end
subgraph 控制层["控制层 (Controllers)"]
CT["controllers/<br>业务协调/信号响应"]
end
subgraph 业务层["业务层"]
SV["services/<br>业务逻辑/事务管理"]
HD["handlers/<br>基础功能处理"]
end
subgraph 数据层["数据层"]
MD["models/<br>ORM模型"]
UT["utils/<br>通用工具"]
end
subgraph 外部系统["外部系统"]
DB[("SQLite<br>数据库")]
EXCEL["Excel文件"]
PRINTER["打印机"]
ACCESS[("Access<br>数据库")]
end
GUI --> VW
CLI --> CT
VW --> CT
WG --> VW
CT --> SV
SV --> HD
HD --> MD
MD --> DB
UT -.-> 所有层
SV --> EXCEL
SV --> PRINTER
SV --> ACCESS
2.2 分层职责¶
层级 |
模块路径 |
职责 |
|---|---|---|
入口层 |
|
GUI 应用启动 / CLI 命令分发 |
视图层 |
|
UI 界面组件,用户交互,信号发送 |
控制层 |
|
业务协调,信号响应,调用服务层 |
服务层 |
|
业务逻辑实现,事务管理 |
处理层 |
|
基础功能处理,Excel 读写、PDF 生成 |
数据层 |
|
ORM 模型定义,数据库表映射 |
工具层 |
|
通用工具类,数据库管理、日志配置 |
2.3 层间通信规则¶
flowchart LR
subgraph 通信方向
V[View] -->|信号/槽| C[Controller]
C -->|方法调用| S[Service]
S -->|方法调用| H[Handler]
H -->|CRUD| M[Model]
M -->|SQL| DB[(Database)]
end
subgraph 禁止规则
V -.->|❌ 直接调用| S
V -.->|❌ 直接操作| M
C -.->|❌ 直接操作| M
C -.->|❌ 直接操作| DB
S -.->|❌ 直接操作| DB
S -.->|❌ 直接操作| V
H -.->|❌ 直接操作| V
H -.->|❌ 直接操作| C
end
规则 |
说明 |
|---|---|
单向依赖 |
上层依赖下层,下层不依赖上层 |
信号通信 |
View ↔ Controller 使用 Qt 信号/槽 |
方法调用 |
Controller → Service → Handler → Model |
禁止跨层 |
View 不能直接调用 Service 或 Model |
禁止反向 |
Service 不能直接操作 View |
三、完整目录结构¶
CertFlow-PySide6/ # 项目根目录
├── README.md # 项目说明
├── pyproject.toml # uv 包管理配置
├── uv.lock # 依赖锁定文件
├── mypy.ini # 类型检查配置
├── pyrightconfig.json # Pyright 配置
├── certflow.spec # PyInstaller 打包配置
├── run.py # 启动脚本(Python)
├── run.bat # 启动脚本(Windows)
├── start.sh # 启动脚本(Linux/Mac)
│
├── src/ # 源代码目录
│ └── certflow/ # 主包
│ ├── __init__.py # 包初始化,版本信息
│ ├── main.py # GUI 程序入口
│ │
│ ├── cli/ # CLI 命令行接口
│ │ ├── __init__.py
│ │ ├── __main__.py # python -m certflow.cli
│ │ ├── base_command.py # 命令基类
│ │ └── commands/ # 各子命令(18+ 个)
│ │ ├── auto_number.py # 自动编号
│ │ ├── backfill_supply_type.py # 回填供货类型
│ │ ├── batch_update.py # 批量更新
│ │ ├── check_consistency.py # 一致性检查
│ │ ├── clean_duplicates.py # 清理重复数据
│ │ ├── copy_cert_info.py # 复制证书信息
│ │ ├── db_backup.py # 数据库备份
│ │ ├── db_sync.py # 数据库同步
│ │ ├── export_access.py # 导出 Access
│ │ ├── export_excel.py # 导出 Excel
│ │ ├── import_certs.py # 导入证书
│ │ ├── import_sales_plan.py # 导入销售计划
│ │ ├── mark_shipped.py # 标记已发货
│ │ ├── print_cert.py # 打印证书
│ │ ├── quick_query.py # 快速查询
│ │ ├── serve.py # 启动服务
│ │ ├── set_year_month.py # 设置年月
│ │ ├── stats.py # 统计信息
│ │ └── watch.py # 文件监控
│ │
│ ├── config/ # 配置模块
│ │ ├── __init__.py
│ │ ├── loader.py # YAML 加载器(支持 !include/环境变量)
│ │ ├── models.py # Pydantic 配置模型
│ │ ├── settings.py # 配置常量读取
│ │ ├── print_config.py # 打印配置加载
│ │ └── ui_config.py # UI 配置加载和样式构建
│ │
│ ├── controllers/ # 控制层
│ │ ├── __init__.py
│ │ ├── base_controller.py # 控制器基类(会话管理)
│ │ ├── certificate_controller.py # 合格证控制器
│ │ ├── history_controller.py # 打印历史控制器
│ │ ├── plaque_query_controller.py # 标牌查询控制器
│ │ ├── printer_controller.py # 打印控制器(QThread)
│ │ ├── query_controller.py # 查询控制器
│ │ ├── report_controller.py # 报告控制器
│ │ ├── scan_controller.py # 扫描件控制器(QThread)
│ │ └── settings_controller.py # 设置控制器
│ │
│ ├── services/ # 服务层
│ │ ├── __init__.py
│ │ ├── auto_number_service.py # 自动编号服务
│ │ ├── cert_export_service.py # 合格证导出(Access/CSV)
│ │ ├── cert_log_service.py # 打印日志服务
│ │ ├── cert_numbering.py # 编号与 Certificate 写入
│ │ ├── cert_print_config.py # 打印配置加载
│ │ ├── cert_print_engine.py # GDI/HTML 打印引擎
│ │ ├── certificate_number_service.py # 证书编号服务
│ │ ├── certificate_print_service.py # 合格证打印服务(QThread)
│ │ ├── certificate_service.py # 合格证 CRUD 服务
│ │ ├── dn_service.py # 口径(DN)解析服务
│ │ ├── pn_service.py # 压力(PN)解析服务
│ │ ├── printer_manager.py # 打印机统一管理
│ │ ├── query_builder.py # 查询构建器
│ │ ├── query_service.py # 高级查询服务(级联筛选)
│ │ ├── report_service.py # 报告服务
│ │ ├── sale_plan_service.py # 销售计划服务(Excel 导入)
│ │ ├── scan_service.py # 扫描件服务(QThread)
│ │ ├── workflow_service.py # 工作流服务
│ │ └── printer/ # 打印机子模块
│ │ ├── __init__.py
│ │ ├── coordinate_converter.py # 坐标转换
│ │ ├── escp_commands.py # ESC/P 命令生成
│ │ ├── lq635kii_printer.py # LQ-635KII 打印机驱动
│ │ ├── print_strategy.py # 打印策略(策略模式)
│ │ ├── printer_service.py # 打印机服务
│ │ └── template_manager.py # 模板管理
│ │
│ ├── handlers/ # 处理层
│ │ ├── __init__.py
│ │ ├── data_cleaner.py # 数据清洗器
│ │ ├── excel_handler.py # Excel 文件处理器
│ │ ├── excel_styler.py # Excel 样式读取(颜色/字体/批注)
│ │ ├── i18n_manager.py # 国际化/语言切换
│ │ ├── id_generator.py # 编号生成器(合格证编号/唯一键)
│ │ ├── report_generator.py # PDF 报告生成器(ReportLab)
│ │ ├── save_handler.py # 数据转换/变更检测(ORM 委托)
│ │ ├── scan_handler.py # 扫描件处理器
│ │ ├── sorter.py # 多键排序器
│ │ ├── styled_excel_importer.py # 带样式 Excel 导入处理器
│ │ ├── template_handler.py # 模板配置管理
│ │ └── theme_manager.py # 主题切换(亮色/暗色)
│ │
│ ├── models/ # 数据层
│ │ ├── __init__.py
│ │ ├── base.py # SQLAlchemy 基类
│ │ ├── auto_number.py # 自动编号规则和计数器
│ │ ├── caliber_mapping.py # 口径映射表
│ │ ├── certificate.py # 合格证模型
│ │ ├── material_grade.py # 材质牌号表
│ │ ├── model_param_mapping.py # 型号参数映射
│ │ ├── param_auto_learn_log.py # 参数自动学习日志
│ │ ├── print_log.py # 打印日志模型(51 字段)
│ │ ├── report_template.py # 报告模板模型
│ │ ├── sale_plan.py # 销售计划模型
│ │ ├── sale_plan_change.py # 销售计划变更历史
│ │ ├── unmatched_certificate.py # 未匹配合格证
│ │ └── vba_mapping.py # VBA 字段映射
│ │
│ ├── utils/ # 工具模块
│ │ ├── __init__.py
│ │ ├── access_db.py # Access 数据库操作(pyodbc)
│ │ ├── database.py # 数据库管理器(引擎/会话/迁移)
│ │ ├── date_utils.py # 日期工具
│ │ ├── image_utils.py # 图像工具(PIL)
│ │ ├── logger.py # 日志管理器(单例/轮转/压缩)
│ │ ├── path_utils.py # 路径工具
│ │ └── status_inference.py # 状态推断
│ │
│ ├── views/ # 视图层
│ │ ├── __init__.py
│ │ ├── main_window.py # 主窗口
│ │ ├── config_view.py # 配置总览视图
│ │ ├── dual_db_query_view.py # 双数据库查询视图
│ │ ├── history_view.py # 打印历史视图
│ │ ├── import_dialog.py # 导入配置对话框
│ │ ├── param_supplement_dialog.py # 参数补充对话框
│ │ ├── print_dialog.py # 打印预览对话框
│ │ ├── print_view.py # 合格证打印视图
│ │ ├── query_view.py # 销售计划查询视图
│ │ ├── report_view.py # 报告生成视图
│ │ ├── scan_view.py # 扫描件生成视图
│ │ ├── settings_view.py # 系统设置视图
│ │ └── query/ # 查询子模块
│ │ ├── __init__.py
│ │ ├── context_menu.py # 右键菜单
│ │ ├── filter_bar.py # 筛选栏
│ │ ├── filter_row.py # 筛选行
│ │ └── toolbar.py # 工具栏
│ │
│ └── widgets/ # 共享 UI 组件
│ ├── __init__.py
│ └── enhanced_table.py # 增强表格控件(排序/筛选/编辑)
│
├── config/ # 配置文件目录
│ ├── config.yaml # 主配置(应用/路径/数据库/日志)
│ ├── userconfig.yaml # 用户配置(最近文件/导入历史/偏好)
│ ├── ui.yaml # UI 配置(窗口/样式/页面布局)
│ ├── views.py # 视图模板配置
│ ├── columns.yaml # 列定义
│ ├── dual_db_display.yaml # 双数据库显示配置
│ ├── imperial_rules.yaml # 英制规则
│ ├── query_fields.yaml # 查询字段定义
│ ├── ui.yaml.example # UI 配置示例
│ ├── userconfig.yaml.example # 用户配置示例
│ ├── schemas/ # JSON Schema 验证
│ │ ├── columns_schema.json
│ │ ├── query_fields_schema.json
│ │ ├── root_schema.json
│ │ ├── ui_schema.json
│ │ └── userconfig_schema.json
│ └── templates/ # 打印模板配置
│ └── coordinates.yaml # 打印坐标模板
│
├── data/ # 数据文件目录
│ ├── db/ # 数据库文件
│ │ ├── certflow.db # 主数据库
│ │ └── SignDB.mdb # Access 遗留数据库
│ ├── images/ # 图片资源
│ ├── templates/ # 模板文件
│ ├── temp/ # 临时文件
│ └── xlsx/ # Excel 示例文件
│
├── database/ # 数据库备份目录
│ ├── backups/ # 自动备份
│ └── certflow*.db # 各版本数据库
│
├── docs/ # 文档目录
│ ├── README.md
│ ├── API_REFERENCE.md
│ ├── CHANGELOG.md
│ ├── CLI.md # CLI 使用文档
│ ├── INSTALLATION.md
│ ├── USER_GUIDE.md
│ ├── PRINT_USER_GUIDE.md
│ ├── TEMPLATE_PRINT.md
│ ├── IMPORT_CLI.md
│ ├── QUERY_CLI.md
│ ├── development/ # 开发文档
│ │ ├── ARCHITECTURE.md
│ │ ├── CODING_STANDARDS.md
│ │ ├── DATA_FLOW.md
│ │ ├── DATA_MODEL_DESIGN.md
│ │ ├── DATABASE_SCHEMA.md
│ │ ├── VBA_FIELD_MAPPING.md
│ │ └── TODO.md
│ ├── deployment/ # 部署文档
│ │ ├── BUILD.md
│ │ └── DOCKER.md
│ └── source/ # Sphinx 文档源
│
├── scripts/ # 脚本工具目录
│ ├── builder/ # 构建脚本
│ │ ├── build.bat
│ │ ├── build.sh
│ │ ├── build_exe.py
│ │ ├── clean.sh
│ │ └── clean_for_build.sh
│ ├── data/ # 数据迁移脚本
│ │ ├── add_progress_tracking_fields.py
│ │ ├── auto_number.py
│ │ ├── backfill_*.py
│ │ ├── import_*.py
│ │ └── update_*.py
│ ├── docs/ # 文档生成脚本
│ │ ├── generate_db_schema.py
│ │ └── tree_structure.py
│ ├── monitor/ # 打印机监控
│ │ ├── printer_monitor.py
│ │ └── analyze_printer_log.py
│ ├── setup/ # 环境初始化
│ │ ├── init.sh
│ │ └── setup_uv.sh
│ ├── sync_databases_cli.py # 数据库同步 CLI
│ └── utils/ # 工具脚本
│
├── tests/ # 测试目录
│ ├── conftest.py # pytest 配置
│ ├── test_access.py
│ ├── test_config.py
│ ├── test_controller.py
│ ├── test_excel_handler.py
│ ├── test_excel_styler.py
│ ├── test_id_generator.py
│ ├── test_import.py
│ ├── test_logger.py
│ ├── test_main.py
│ ├── test_print_log_51fields.py
│ ├── test_printer_controller.py
│ ├── test_query_builder.py
│ ├── test_query_service.py
│ ├── test_query_view.py
│ ├── test_sale_plan_service.py
│ ├── test_settings_advanced.py
│ ├── test_ui_config.py
│ └── export_unique_keys.py
│
├── logs/ # 日志目录
│ ├── certflow.log # 当前日志
│ └── certflow.*.log.zip # 压缩的历史日志
│
├── output/ # 输出目录
│ ├── backups/ # 手动备份
│ ├── certificates/ # 导出的合格证
│ ├── images/ # 生成的图片
│ ├── logs/ # 导出日志
│ ├── nameplates/ # 标牌
│ └── reports/ # 生成的报告
│
├── resources/ # 资源文件
│ ├── backgrounds/ # 背景图
│ │ ├── cn_en_bg.png
│ │ ├── full_chinese_bg.png
│ │ └── ru_en_bg.png
│ ├── fonts/ # 字体文件
│ ├── icons/ # 图标
│ └── templates/ # 报告模板
│
├── temp/ # 临时文件目录
├── htmlcov/ # 测试覆盖率报告
├── coverage_improvement_plan.md # 覆盖率改进计划
└── migration_preview.xlsx # 迁移预览
四、核心数据流¶
4.1 系统整体数据流¶
flowchart LR
subgraph 输入
EXCEL[Excel 文件]
ACCESS[Access 数据库]
USER[用户操作]
end
subgraph 处理
IMP[导入处理]
GEN[生成处理]
PRINT[打印处理]
QUERY[查询处理]
end
subgraph 存储
DB[(SQLite 数据库)]
CONFIG[配置文件]
end
subgraph 输出
CERT[合格证]
REPORT[报告 PDF]
SCAN[扫描件 PNG]
LOG[日志文件]
end
EXCEL --> IMP
ACCESS --> IMP
USER --> GEN
USER --> PRINT
USER --> QUERY
IMP --> DB
IMP --> CONFIG
GEN --> DB
PRINT --> DB
QUERY --> DB
DB --> CERT
DB --> REPORT
DB --> SCAN
DB --> LOG
4.2 销售计划导入流程¶
flowchart TD
A[用户选择 Excel 文件] --> B[ImportConfigDialog<br>配置参数]
B --> C[SalePlanService<br>.import_from_excel_with_config]
C --> D[StyledExcelImporter<br>.read_styled_excel]
D --> E[DataCleaner<br>.clean_data]
E --> F[Sorter<br>.sort_records]
F --> G[SaveHandler<br>.save_records_with_stats]
G --> H{检查唯一键}
H -->|不存在| I[新增记录]
H -->|已存在| J[更新记录]
I --> K[保存到 SalePlan 表]
J --> L[记录变更历史]
K --> M[返回导入统计]
L --> M
调用链路示例:
# ========== 调用链路示例 ==========
# View层: views/import_dialog.py
class ImportConfigDialog(QDialog):
def on_import_clicked(self):
self.import_requested.emit(config)
# Controller层: controllers/query_controller.py
class QueryController:
def on_import_requested(self, config):
result = self.sale_plan_service.import_from_excel_with_config(
file_path=config.file_path,
sheet_name=config.sheet_name,
header_row=config.header_row,
column_mapping=config.column_mapping
)
self.view.show_import_result(result)
# Service层: services/sale_plan_service.py
class SalePlanService:
def import_from_excel_with_config(self, **kwargs):
styled_data = StyledExcelImporter.read_styled_excel(...)
cleaned_data = DataCleaner.clean_data(styled_data)
sorted_data = Sorter.sort_records(cleaned_data)
stats = SaveHandler.save_records_with_stats(
records=sorted_data,
model_class=SalePlan,
unique_key='unique_key'
)
return stats
4.3 合格证生成与打印流程¶
flowchart TD
A[用户选择销售计划] --> B[选择编号规则]
B --> C[CertificatePrintService<br>启动打印线程]
C --> D[CertNumberingService<br>.auto_number]
D --> E[按 supply_type 分组]
E --> F[生成编号: 前缀+日期+流水号+后缀]
F --> G[CertNumberingService<br>.create_certificates]
G --> H[写入 Certificate 表]
H --> I[check_and_supplement_params]
I --> J{参数完整?}
J -->|否| K[弹窗补充: 口径/压力/温度/介质/材质/工号]
K --> L[保存补充参数]
J -->|是| L
L --> M[批量打印循环]
M --> N[CertPrintEngine<br>.print_single - GDI 精确打印]
N --> O{打印成功?}
O -->|是| P[记录打印日志]
O -->|否| Q[CertPrintEngine<br>.print_html_fallback - 降级]
Q --> P
P --> R[CertLogService<br>.auto_save - 51字段日志]
R --> S[更新打印状态]
S --> T[完成]
调用链路示例:
# ========== 调用链路示例 ==========
# Service层: services/certificate_print_service.py
class CertificatePrintService(QThread):
def run(self):
# 1. 编号服务
cert_numbering = CertNumberingService()
cert_data = cert_numbering.auto_number(
sale_plan_ids=self.selected_ids,
rule_id=self.rule_id
)
# 2. 写入Certificate表
for data in cert_data:
certificate = Certificate(
certificate_number=data['cert_number'],
sale_plan_id=data['sale_plan_id'],
supply_type=data['supply_type']
)
self.db_session.add(certificate)
# 3. 补充参数(弹窗)
self.check_and_supplement_params(cert_data)
# 4. 批量打印
print_engine = CertPrintEngine()
for cert in certificates:
print_engine.print_single(cert, self.template, self.printer_name)
CertLogService.auto_save(cert, print_result)
self.print_completed.emit(results)
4.4 查询与删除流程¶
flowchart TD
A[用户输入筛选条件] --> B[QueryView 收集筛选条件]
B --> C[QueryController<br>.on_search_requested]
C --> D[QueryService.query]
D --> E[构建 SQLAlchemy 查询]
E --> F[应用过滤器]
F --> G[排序]
G --> H[分页]
H --> I[返回 QueryResult]
I --> J[更新表格显示]
K[用户选择记录] --> L[点击删除]
L --> M[QueryController<br>.on_delete_requested]
M --> N{检查关联证书}
N -->|有| O[级联删除]
N -->|无| P[直接删除]
O --> Q[删除 Certificate]
Q --> R[删除 SalePlan]
P --> R
R --> S[事务提交]
S --> T[刷新查询结果]
调用链路示例:
# ========== 调用链路示例 ==========
# Service层: services/query_service.py
class QueryService:
def query(self, filters, page, page_size):
query = self.db_session.query(SalePlan)
for field, value in filters.items():
if value:
if field == 'customer_name':
query = query.filter(SalePlan.customer_name.like(f'%{value}%'))
elif field == 'create_date_range':
query = query.filter(SalePlan.create_date.between(value[0], value[1]))
query = query.order_by(SalePlan.create_date.desc())
total = query.count()
data = query.offset((page - 1) * page_size).limit(page_size).all()
return QueryResult(total=total, data=data, page=page)
def delete_with_certificates(self, ids):
# 级联删除
self.db_session.query(Certificate).filter(
Certificate.sale_plan_id.in_(ids)
).delete(synchronize_session=False)
self.db_session.query(SalePlan).filter(
SalePlan.id.in_(ids)
).delete(synchronize_session=False)
4.5 扫描件生成流程¶
flowchart TD
A[用户选择合格证] --> B[选择模板]
B --> C[ScanController 启动 QThread]
C --> D[ScanService.run]
D --> E[加载证书数据]
E --> F[循环处理每个证书]
F --> G[ScanImageHandler.generate]
G --> H[ReportLab 生成 PDF]
H --> I[PyMuPDF 转换为图像]
I --> J[ScanLayoutHandler.layout]
J --> K[添加水印]
K --> L[添加边框]
L --> M[保存为 PNG 文件]
M --> N[更新进度]
N --> O{还有证书?}
O -->|是| F
O -->|否| P[打开输出目录]
调用链路示例:
# ========== 调用链路示例 ==========
# Service层: services/scan_service.py
class ScanService(QThread):
def run(self):
certificates = self.load_certificates(self.cert_ids)
image_handler = ScanImageHandler()
layout_handler = ScanLayoutHandler()
for idx, cert in enumerate(certificates):
image = image_handler.generate(
certificate=cert,
template=self.template,
dpi=300
)
final_image = layout_handler.layout(
image=image,
watermark=self.watermark,
border=self.border
)
output_path = Path(self.output_dir) / f"{cert.certificate_number}.png"
final_image.save(output_path)
self.progress_updated.emit(idx + 1, len(certificates))
QDesktopServices.openUrl(QUrl.fromLocalFile(str(self.output_dir)))
4.6 主题切换流程¶
flowchart TD
A[用户点击 🌙/☀️] --> B[MainWindow.on_theme_toggle]
B --> C[SettingsController.switch_theme]
C --> D[ThemeManager.apply_theme]
D --> E[加载主题 QSS]
E --> F[QApplication.setStyleSheet]
F --> G[更新配置]
C --> H[更新按钮图标]
H --> I[保存用户配置]
C --> J[_sync_timer_from_prefs]
J --> K[同步自动保存定时器]
K --> L[界面实时更新]
五、CLI 命令行接口¶
5.1 CLI 架构¶
flowchart TB
subgraph CLI入口
CMD["python -m certflow.cli"]
end
subgraph 命令注册
REG["命令注册表"]
end
subgraph 命令实现
C1["auto_number"]
C2["import_sales_plan"]
C3["print_cert"]
C4["db_sync"]
C5["stats"]
C6["... 18+ 命令"]
end
subgraph 共享服务
SV["Services 层"]
DB[("数据库")]
end
CMD --> REG
REG --> C1 & C2 & C3 & C4 & C5 & C6
C1 --> SV
C2 --> SV
C3 --> SV
C4 --> SV
C5 --> SV
SV --> DB
5.2 核心命令列表¶
分类 |
命令 |
用途 |
示例 |
|---|---|---|---|
数据导入 |
|
导入销售计划 |
|
|
导入合格证 |
|
|
|
回填供货类型 |
|
|
打印管理 |
|
打印合格证 |
|
|
自动编号 |
|
|
数据管理 |
|
备份数据库 |
|
|
同步数据库 |
|
|
|
清理重复数据 |
|
|
|
一致性检查 |
|
|
数据导出 |
|
导出 Excel |
|
|
导出 Access |
|
|
查询统计 |
|
快速查询 |
|
|
统计信息 |
|
|
数据维护 |
|
批量更新 |
|
|
标记已发货 |
|
|
|
复制证书信息 |
|
|
服务 |
|
启动服务 |
|
|
文件监控 |
|
5.3 命令基类设计¶
# cli/base_command.py
from abc import ABC, abstractmethod
import argparse
class BaseCommand(ABC):
"""CLI 命令基类"""
name: str = ""
help: str = ""
@abstractmethod
def add_arguments(self, parser: argparse.ArgumentParser) -> None:
"""添加命令行参数"""
pass
@abstractmethod
def execute(self, args: argparse.Namespace) -> int:
"""执行命令,返回退出码"""
pass
def setup(self) -> None:
"""命令执行前的初始化"""
pass
def teardown(self) -> None:
"""命令执行后的清理"""
pass
5.4 命令实现示例¶
# cli/commands/import_sales_plan.py
from ..base_command import BaseCommand
class ImportSalesPlanCommand(BaseCommand):
name = "import_sales_plan"
help = "从 Excel 文件导入销售计划"
def add_arguments(self, parser):
parser.add_argument("-f", "--file", required=True, help="Excel 文件路径")
parser.add_argument("--sheet", default="Sheet1", help="工作表名称")
parser.add_argument("--header-row", type=int, default=1, help="表头行号")
parser.add_argument("--dry-run", action="store_true", help="预览模式")
def execute(self, args):
service = SalePlanService()
result = service.import_from_excel_with_config(
file_path=args.file,
sheet_name=args.sheet,
header_row=args.header_row,
dry_run=args.dry_run
)
print(f"导入完成: {result}")
return 0
5.5 全局选项¶
选项 |
说明 |
默认值 |
|---|---|---|
|
指定配置文件路径 |
|
|
指定数据库路径 |
|
|
显示详细日志 |
|
|
静默模式 |
|
|
显示版本信息 |
- |
5.6 环境变量¶
变量 |
说明 |
默认值 |
|---|---|---|
|
配置文件路径 |
|
|
数据库路径 |
|
|
日志级别 |
|
5.7 使用示例¶
# 查看帮助
python -m certflow.cli --help
# 导入销售计划(预览模式)
python -m certflow.cli import_sales_plan -f data.xlsx --dry-run
# 批量打印
python -m certflow.cli print_cert --ids 1,2,3,4,5
# 数据库备份 + 同步
python -m certflow.cli db_backup
python -m certflow.cli db_sync --source backup.db
# 统计查询
python -m certflow.cli stats --type daily --month 2026-06
5.8 错误码¶
代码 |
说明 |
|---|---|
0 |
成功 |
1 |
通用错误 |
2 |
配置错误 |
3 |
数据库错误 |
4 |
文件错误 |
5 |
打印错误 |
6 |
参数错误 |
7 |
权限错误 |
六、配置管理设计¶
6.1 配置文件结构¶
flowchart LR
subgraph 配置源
ENV[环境变量]
YAML[YAML 文件]
DEFAULT[代码默认值]
end
subgraph 配置加载
LOADER[ConfigLoader]
SCHEMA[Schema 验证]
CACHE[内存缓存]
end
subgraph 配置文件
C1[config.yaml<br>主配置]
C2[userconfig.yaml<br>用户配置]
C3[ui.yaml<br>UI 配置]
C4[columns.yaml<br>列定义]
C5[query_fields.yaml<br>查询字段]
C6[templates/coordinates.yaml<br>打印模板]
end
ENV --> LOADER
YAML --> LOADER
DEFAULT --> LOADER
LOADER --> SCHEMA
SCHEMA --> CACHE
C1 & C2 & C3 & C4 & C5 & C6 --> LOADER
6.2 配置文件详情¶
文件 |
用途 |
关键配置项 |
|---|---|---|
|
主配置 |
应用名称、版本、数据库路径、日志级别 |
|
用户配置 |
最近文件列表、导入历史、窗口位置 |
|
UI 配置 |
主题、字体、布局、颜色方案 |
|
列定义 |
表格列名、宽度、可见性、排序 |
|
查询字段 |
筛选条件字段、类型、可选值 |
|
双数据库显示 |
对比显示配置 |
|
英制规则 |
英制单位转换规则 |
|
打印模板 |
打印坐标、字体、尺寸 |
6.3 配置加载特性¶
特性 |
说明 |
实现方式 |
|---|---|---|
惰性加载 |
配置只在首次访问时加载 |
|
缓存机制 |
加载后缓存在内存中 |
|
环境变量替换 |
支持 |
正则表达式替换 |
文件包含 |
支持 |
YAML 自定义构造器 |
热重载 |
打印配置支持热重载 |
|
Schema 验证 |
配置结构校验 |
JSON Schema |
6.4 配置优先级¶
flowchart LR
ENV[环境变量<br>最高优先级] --> APP[应用配置]
YAML[YAML 配置<br>中等优先级] --> APP
DEFAULT[代码默认值<br>最低优先级] --> APP
6.5 配置加载器实现¶
# config/loader.py
class ConfigLoader:
"""配置加载器,支持惰性加载、缓存、环境变量替换"""
_instance = None
_config_cache = {}
@classmethod
def get_instance(cls) -> "ConfigLoader":
if cls._instance is None:
cls._instance = cls()
return cls._instance
def load(self, name: str) -> dict:
"""加载指定配置"""
if name in self._config_cache:
return self._config_cache[name]
path = self._get_config_path(name)
content = self._read_file(path)
content = self._resolve_env_vars(content)
content = self._resolve_includes(content)
config = yaml.safe_load(content)
self._validate_schema(config, name)
self._config_cache[name] = config
return config
def _resolve_env_vars(self, content: str) -> str:
"""解析环境变量 ${VAR:default}"""
pattern = r'\${([^}:]+)(?::([^}]*))?}'
def replacer(match):
var_name = match.group(1)
default = match.group(2) or ''
return os.environ.get(var_name, default)
return re.sub(pattern, replacer, content)
七、数据库设计¶
7.1 ER 图¶
erDiagram
sale_plans ||--o{ certificates : "生成"
sale_plans ||--o{ sale_plan_changes : "变更历史"
certificates ||--o{ print_logs : "打印记录"
certificates }o--|| material_grades : "材质"
certificates }o--|| caliber_mappings : "口径"
sale_plans {
integer id PK
string unique_key UK
string customer_name
string product_model
string supply_type
date create_date
string execution_status
}
certificates {
integer id PK
string certificate_number UK
integer sale_plan_id FK
string supply_type
string caliber
string pressure
string material_grade
string temperature
string medium
date print_date
}
print_logs {
integer id PK
integer certificate_id FK
string certificate_number
string printer_name
datetime print_time
string status
text error_message
}
auto_number_rules ||--o{ auto_number_counters : "计数"
auto_number_rules {
integer id PK
string name
string prefix
string date_format
integer padding
string suffix_pattern
string reset_period
}
auto_number_counters {
integer id PK
integer rule_id FK
string group_key
integer current_value
date last_reset_date
}
7.2 核心表说明¶
表名 |
用途 |
关键字段 |
说明 |
|---|---|---|---|
|
销售计划主表 |
|
核心业务数据表 |
|
变更历史 |
|
自动记录变更 |
|
合格证表 |
|
合格证数据 |
|
打印日志(51字段) |
|
兼容 Access SignTb |
|
材质牌号 |
|
材质字典表 |
|
口径映射 |
|
口径字典表 |
|
型号参数映射 |
|
自动补全参数 |
|
参数学习日志 |
|
自动学习记录 |
|
编号规则 |
|
编号规则配置 |
|
编号计数器 |
|
流水号计数 |
7.3 自动编号特性¶
特性 |
说明 |
|---|---|
分组计数 |
按产品型号/订单号/供货类型分组 |
周期重置 |
按日/月/年重置流水号 |
自定义格式 |
前缀 + 日期 + 流水号 + 后缀 |
后缀判定 |
按 |
编号格式示例:
FL202606170001L
││ ││ ││││ └─ 后缀(L = 法兰)
││ ││ │││└─── 流水号(0001)
││ ││ ││└──── 日(17)
││ ││ │└───── 月(06)
││ ││ └────── 年(2026)
││ │└─────────── 前缀(FL = 法兰)
││ └──────────── 分隔符
7.4 数据库管理器¶
# utils/database.py
class DatabaseManager:
"""数据库管理器(单例模式)"""
_instance = None
_engine = None
_session_factory = None
@classmethod
def get_instance(cls) -> "DatabaseManager":
if cls._instance is None:
cls._instance = cls()
return cls._instance
def initialize(self, connection_string: str) -> None:
"""初始化数据库连接"""
self._engine = create_engine(connection_string, echo=False)
self._session_factory = sessionmaker(bind=self._engine)
Base.metadata.create_all(self._engine)
def get_session(self) -> Session:
"""获取数据库会话"""
return self._session_factory()
def backup(self, backup_path: str) -> None:
"""备份数据库"""
shutil.copy2(self._engine.url.database, backup_path)
def migrate(self, migration_script: str) -> None:
"""执行数据库迁移"""
with self.get_session() as session:
session.execute(text(migration_script))
session.commit()
八、日志设计¶
8.1 日志架构¶
flowchart LR
APP[应用程序] --> LOG[LoggerManager 单例]
LOG --> CONSOLE[控制台输出<br>INFO 级别 彩色]
LOG --> FILE[文件输出<br>DEBUG 级别]
FILE --> ROTATE[每天轮转]
ROTATE --> COMPRESS[压缩为 ZIP]
COMPRESS --> RETENTION[保留 30 天]
8.2 日志配置¶
特性 |
说明 |
|---|---|
模式 |
单例模式,全局唯一日志管理器 |
控制台输出 |
INFO 级别,彩色格式(适用于开发调试) |
文件输出 |
DEBUG 级别,每天轮转 |
保留策略 |
保留 30 天,自动压缩为 ZIP |
日志文件 |
|
8.3 日志管理器实现¶
# utils/logger.py
from loguru import logger
import sys
class LoggerManager:
"""日志管理器(单例模式)"""
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance._setup()
return cls._instance
def _setup(self):
"""配置日志"""
# 移除默认处理器
logger.remove()
# 控制台输出(彩色)
logger.add(
sys.stdout,
format="<green>{time:YYYY-MM-DD HH:mm:ss}</green> | <level>{level: <8}</level> | <cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> - <level>{message}</level>",
level="INFO",
colorize=True
)
# 文件输出(每天轮转)
logger.add(
"logs/certflow.log",
format="{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {name}:{function}:{line} - {message}",
level="DEBUG",
rotation="1 day",
retention="30 days",
compression="zip"
)
def get_logger(self):
return logger
8.4 日志使用示例¶
from certflow.utils.logger import LoggerManager
# 获取日志实例
log = LoggerManager().get_logger()
# 不同级别日志
log.debug("调试信息")
log.info("系统启动")
log.warning("配置缺失,使用默认值")
log.error("打印失败", exc_info=True)
log.success("导入完成")
# 带上下文的日志
log.bind(user="admin").info("用户登录")
九、测试设计¶
9.1 测试架构¶
flowchart TB
subgraph 测试层
UNIT[单元测试<br>test_*.py]
INT[集成测试<br>test_*_integration.py]
end
subgraph 测试框架
PYT[pytest]
COV[coverage.py]
FIX[fixtures]
end
subgraph 被测试模块
SRC[src/certflow/]
end
UNIT --> PYT
INT --> PYT
PYT --> COV
PYT --> FIX
FIX --> SRC
COV --> REPORT[覆盖率报告<br>htmlcov/]
9.2 测试文件结构¶
测试文件 |
测试目标 |
状态 |
|---|---|---|
|
配置加载器、配置模型 |
✅ |
|
控制器逻辑 |
✅ |
|
Excel 文件处理 |
✅ |
|
Excel 样式读取 |
✅ |
|
查询服务 |
✅ |
|
查询构建器 |
✅ |
|
销售计划服务 |
✅ |
|
打印控制器 |
✅ |
|
打印日志 51 字段 |
✅ |
|
ID 生成器 |
✅ |
|
日志系统 |
✅ |
|
导入功能 |
✅ |
|
主入口 |
✅ |
|
Access 数据库操作 |
✅ |
|
UI 配置 |
✅ |
|
高级设置 |
✅ |
|
查询视图 |
✅ |
9.3 测试 Fixtures¶
# tests/conftest.py
import pytest
from src.certflow.utils.database import DatabaseManager
@pytest.fixture
def db_session():
"""测试用数据库会话"""
db = DatabaseManager.get_instance()
db.initialize("sqlite:///:memory:") # 使用内存数据库
session = db.get_session()
yield session
session.rollback()
session.close()
@pytest.fixture
def sample_sale_plan():
"""示例销售计划数据"""
return {
"customer_name": "测试客户",
"product_model": "DN100",
"supply_type": "法兰",
"quantity": 10
}
@pytest.fixture
def config_loader():
"""配置加载器"""
from src.certflow.config.loader import ConfigLoader
return ConfigLoader.get_instance()
9.4 运行测试¶
# 运行所有测试
uv run pytest tests/
# 运行指定测试文件
uv run pytest tests/test_query_service.py
# 运行指定测试函数
uv run pytest tests/test_query_service.py::test_query_with_filters
# 运行测试并显示详细输出
uv run pytest -v tests/
# 运行测试并显示打印输出
uv run pytest -s tests/
# 运行测试并生成覆盖率报告
uv run pytest --cov=src/certflow tests/
# 生成 HTML 覆盖率报告
uv run pytest --cov=src/certflow --cov-report=html tests/
9.5 覆盖率目标¶
模块 |
目标 |
说明 |
|---|---|---|
|
≥ 80% |
核心业务逻辑 |
|
≥ 75% |
基础功能处理器 |
|
≥ 70% |
控制器(含 UI 交互) |
|
≥ 80% |
工具类 |
|
≥ 90% |
数据模型(简单) |
|
≥ 70% |
配置管理 |
整体 |
≥ 70% |
全模块平均 |
十、扩展点¶
10.1 扩展点总览¶
flowchart LR
subgraph 扩展方式
E1[继承基类]
E2[配置驱动]
E3[注册机制]
E4[新增文件]
end
subgraph 扩展点
P1[报告类型]
P2[编号规则]
P3[数据模型]
P4[处理器]
P5[UI 主题]
P6[视图]
P7[控制器]
P8[服务]
P9[CLI 命令]
end
E1 --> P1 & P3 & P7 & P8
E2 --> P2 & P5
E3 --> P9
E4 --> P4 & P6
10.2 扩展点详情¶
扩展点 |
方式 |
示例代码 |
|---|---|---|
新增报告类型 |
继承 |
|
新增编号规则 |
数据库配置 |
|
新增数据模型 |
继承 |
|
新增处理器 |
添加到 |
|
UI 主题定制 |
修改 |
|
新增视图 |
添加到 |
|
新增控制器 |
添加到 |
|
新增服务 |
添加到 |
|
新增 CLI 命令 |
添加到 |
|
10.3 扩展示例¶
新增报告类型¶
# handlers/report_generator.py
class CustomReportGenerator(ReportGenerator):
"""自定义报告生成器"""
def generate(self, data: dict, output_path: str) -> str:
"""生成自定义报告"""
pdf = canvas.Canvas(output_path)
# 自定义绘制逻辑
pdf.drawString(100, 800, "自定义报告")
pdf.save()
return output_path
新增 CLI 命令¶
# cli/commands/export_custom.py
from ..base_command import BaseCommand
class ExportCustomCommand(BaseCommand):
name = "export_custom"
help = "导出自定义格式"
def add_arguments(self, parser):
parser.add_argument("-o", "--output", required=True, help="输出路径")
def execute(self, args):
# 实现导出逻辑
print(f"导出到: {args.output}")
return 0
十一、技术栈总览¶
分类 |
组件 |
技术 |
版本 |
用途 |
|---|---|---|---|---|
GUI |
框架 |
PySide6 |
≥6.5 |
跨平台桌面界面 |
ORM |
数据库 |
SQLAlchemy |
≥2.0 |
数据库 ORM |
生成 |
ReportLab |
≥4.0 |
报告 PDF 生成 |
|
Excel |
读取 |
pandas |
≥2.0 |
数据处理 |
样式 |
openpyxl |
≥3.1 |
Excel 样式保留 |
|
日志 |
管理 |
loguru |
≥0.7 |
统一日志管理 |
配置 |
解析 |
PyYAML |
≥6.0 |
YAML 配置解析 |
包管理 |
依赖 |
uv |
≥0.5 |
Python 包管理 |
测试 |
框架 |
pytest |
≥7.0 |
单元测试 |
覆盖率 |
pytest-cov |
≥4.0 |
覆盖率报告 |
|
类型检查 |
静态 |
mypy |
≥1.0 |
类型检查 |
静态 |
pyright |
≥1.0 |
类型检查 |
|
打包 |
可执行 |
PyInstaller |
≥5.0 |
打包为 exe |
文档 |
生成 |
Sphinx |
≥5.0 |
文档生成 |
数据库 |
Access |
pyodbc |
≥4.0 |
Access 数据库操作 |
图像 |
处理 |
Pillow |
≥10.0 |
图像处理 |
十二、附录¶
12.1 缩略语¶
缩略语 |
全称 |
说明 |
|---|---|---|
ORM |
Object-Relational Mapping |
对象关系映射 |
QSS |
Qt Style Sheets |
Qt 样式表 |
ESC/P |
Epson Standard Code for Printers |
爱普生打印控制命令 |
GDI |
Graphics Device Interface |
Windows 图形设备接口 |
DN |
Diameter Nominal |
公称通径 |
PN |
Pressure Nominal |
公称压力 |
CLI |
Command Line Interface |
命令行接口 |
GUI |
Graphical User Interface |
图形用户界面 |
CRUD |
Create, Read, Update, Delete |
增删改查 |
MVC |
Model-View-Controller |
模型-视图-控制器 |
12.2 设计模式应用¶
设计模式 |
应用场景 |
示例 |
|---|---|---|
单例模式 |
日志、配置、数据库 |
|
工厂模式 |
处理器创建 |
|
策略模式 |
打印策略 |
|
观察者模式 |
信号/槽通信 |
Qt 信号机制 |
委托模式 |
数据保存 |
|
模板方法模式 |
报告生成 |
|
命令模式 |
CLI 命令 |
|
12.3 性能优化设计¶
优化点 |
方式 |
说明 |
|---|---|---|
批量操作 |
|
批量插入/更新 |
分页查询 |
|
大数据量分页 |
配置缓存 |
|
避免重复加载 |
QSS 缓存 |
|
避免重复渲染 |
异步处理 |
QThread |
耗时操作不阻塞 UI |
延迟加载 |
|
按需加载 |
12.4 安全设计¶
安全项 |
方式 |
说明 |
|---|---|---|
SQL 注入防护 |
SQLAlchemy 参数化查询 |
防止 SQL 注入 |
事务管理 |
|
数据一致性 |
文件路径验证 |
|
防止路径遍历 |
日志脱敏 |
自定义过滤器 |
敏感信息脱敏 |
输入验证 |
Pydantic 模型 |
配置数据验证 |
12.5 版本历史¶
版本 |
日期 |
变更说明 |
|---|---|---|
v1.0 |
2026-06-01 |
初始版本 |
v2.0 |
2026-06-17 |
同步实际项目结构:新增 CLI、widgets、测试模块,完善流程图 |
📌 文档维护:本文档应与
src/certflow/代码保持同步。如有架构变更,请及时更新此文档。