"""配置数据模型
使用 Pydantic 定义配置数据结构,提供类型验证和自动补全.
"""
from __future__ import annotations
import tempfile
from typing import Any
from pydantic import BaseModel, Field, field_validator
class _StrictModel(BaseModel):
"""所有配置子模型的基类:extra=forbid(§15.3 阶段3 严格校验收官)。
任何未在模型中声明的键,会在启动期 ConfigLoader.load()(内部以原始合并 dict
构造 RootConfig)触发 ValidationError,从机制上杜绝「配置静默失效」——
即键写错/漏建模却被 _cfg 读取、永远取默认值的隐性缺陷(见 STRICT_VALIDATION_AUDIT.md)。
例外(不继承或覆盖为 allow):
- UIConfig:作为 ui 段运行时兜底容器,保守保持 extra=allow,便于 ui.yaml 后续扩展;
其顶层键的 forbid 校验由独立的 ``UIConfigModel``(ui_config.py,Phase 2.4 新增)
在 ``UIConfig.load`` 加载期把关,二者分工:容器宽松、闸门严格。
- RootConfig:顶层已于任务 2.3 闭环为 extra=forbid(ui.yaml 经 !include 并入的 12 个
UI 顶层键已全部建模为宽松 dict/list 字段,由 forbid 闸门把关)。
说明:前两批(feat/wf15-strict-forbid[-batch2])已显式开 forbid 的子模型,其自身
model_config 与本基类一致(冗余但无害),保留以记录 4 个静默失效 bug 的修复历史。
"""
model_config = {"extra": "forbid"}
[文档]
class AppConfig(_StrictModel):
"""应用基础配置模型
定义应用程序的基本信息,包括名称、版本、组织等元数据.
Attributes:
name: 应用程序名称,默认为"CertFlow"
version: 应用程序版本号,默认为"2.0.0"
year: 版权年份,默认为2026
organization: 组织名称,默认为"CertFlow Team"
debug: 是否启用调试模式,默认为False
"""
# §15.3 阶段3:严格校验
model_config = {"extra": "forbid"}
name: str = "CertFlow"
version: str = "2.0.0"
year: int = 2026
organization: str = "CertFlow Team"
debug: bool = False
class DatabasePaths(_StrictModel):
"""数据库路径配置模型
定义数据库文件的存储目录和文件名,以及调试库/基线库路径.
Attributes:
dir: 数据库文件所在目录,默认为"database"
name: 数据库文件名,默认为"certflow.db"
debug: 调试库路径,默认为"database/debug/certflow-debug.db"
baseline_local: 本地私有基线库路径,默认为"database/baseline.db"
baseline_sample: 匿名脱敏样本基线库路径(Git LFS),默认为"database/samples/baseline.sample.db"
"""
dir: str = "database"
name: str = "certflow.db"
debug: str = "database/debug/certflow-debug.db"
baseline_local: str = "database/baseline.db"
baseline_sample: str = "database/samples/baseline.sample.db"
class TempPaths(_StrictModel):
"""临时目录配置模型
定义各类临时文件的存储子目录.
Attributes:
base: 临时文件根目录,默认为系统临时目录(tempfile.gettempdir(),云环境正确)。
本地开发若想在项目中观察临时文件,于 paths.local.yaml 写 paths.temp.base
覆盖为项目内相对/绝对目录即可(见 paths.local.yaml.example)。
excel: Excel临时文件目录,默认为"excel"
reports: 报告临时文件目录,默认为"reports"
images: 图片临时文件目录,默认为"images"
scans: 扫描件临时文件目录,默认为"scans"
"""
base: str = tempfile.gettempdir()
excel: str = "excel"
reports: str = "reports"
images: str = "images"
scans: str = "scans"
[文档]
class TemplatePaths(_StrictModel):
"""模板路径配置模型
定义各类模板文件的存储路径.
Attributes:
base: 模板文件根目录,默认为"data/templates/"
certificate_chinese: 中文合格证模板路径
certificate_bilingual: 中英文合格证模板路径
certificate_russian: 俄文合格证模板路径
test_report_small: 试压报告模板(少)路径
test_report_medium: 试压报告模板(中)路径
quality_cert_file: 质保书模板路径
report_master: 报告母版工作簿路径(T6 配置化,原硬编码 data/xlsx/*.xlsx)
"""
base: str = "data/templates/"
certificate_chinese: str = "data/templates/中文合格证模板.xlsx"
certificate_bilingual: str = "data/templates/中英文合格证模板.xlsx"
certificate_russian: str = ""
test_report_small: str = ""
test_report_medium: str = ""
quality_cert_file: str = ""
report_master: str = "data/xlsx/各种报告的xlsx文件模板.xlsx"
class ReportFolderConfig(_StrictModel):
"""项目文件夹命名配置(逐字复用 VBA Mod_GCreatFloder.新建文件夹,蓝图 §7.2)
全部由 config.yaml 驱动,后期调整归档目录结构只改 yaml、不搜改代码:
pattern: 目录相对 root 的命名模板,占位符 {ym}/{rq}/{customer}/{project}
tokens: 占位符求值模板(str.format,可用 {year}{month:02d}{day:02d})
empty_project_placeholder: 项目名称为空时的兜底文本
"""
pattern: str = "{ym}计划项目资料/{rq}_{customer}_{project}项目资料"
tokens: dict[str, str] = Field(default_factory=dict)
empty_project_placeholder: str = "【空白项目名称】"
class ReportArchiveConfig(_StrictModel):
"""报告归档配置(VBA 风格文件夹 + 电子印章,蓝图 §7)
Attributes:
root: 归档根目录(VBA 的 D:\\F\\备份\\...\\质量证明书\\ 统一为可配置单根)
folder: 项目文件夹命名配置(pattern/tokens/兜底文本,yaml 驱动)
seal_default: 全局默认电子印章(PNG/透明底);空字符串则不嵌章
seal: 印章在 PDF 上的叠加位置(page=-1 末页;x/y/width 单位 PDF 点)
"""
root: str = "output/项目资料"
folder: ReportFolderConfig = Field(default_factory=ReportFolderConfig)
seal_default: str = ""
seal: dict[str, Any] = Field(default_factory=dict)
[文档]
class PathsConfig(_StrictModel):
"""路径配置集合模型
聚合所有文件路径相关的配置,包括数据库、临时文件、日志等路径.
Attributes:
database: 数据库路径配置
temp: 临时文件路径配置
logs: 日志文件目录,默认为"logs"
resources: 资源文件目录,默认为"resources"
templates: 模板文件路径配置
sales_plan: 销售计划文件路径映射(V2)
qualified_list: 合格清单文件路径映射(V2)
nameplate_db: 铭牌数据库路径映射(V2)
quality_cert: 质量证书路径映射(V2)
transfer_docs: 交接文档路径映射(V2)
backup: 备份文件路径映射(V2)
output: 输出文件路径映射(V2)
excel_layout: Excel布局配置(V2)
images: 图片文件路径映射(V2)
"""
# §15.3 阶段3:严格校验
model_config = {"extra": "forbid"}
database: DatabasePaths = Field(default_factory=DatabasePaths)
temp: TempPaths = Field(default_factory=TempPaths)
logs: str = "logs"
resources: str = "resources"
templates: TemplatePaths = Field(default_factory=TemplatePaths)
# V2 新增路径
sales_plan: dict[str, str] | None = None
qualified_list: dict[str, str] | None = None
nameplate_db: dict[str, str] | None = None
quality_cert: dict[str, str] | None = None
transfer_docs: dict[str, str] | None = None
backup: dict[str, str] | None = None
output: dict[str, str] | None = None
excel_layout: dict[str, Any] | None = None
images: dict[str, str] | None = None
fonts: dict[str, Any] | None = None
# 报告归档(VBA 风格文件夹 + 电子印章,蓝图 §7)
report_archive: ReportArchiveConfig = Field(default_factory=lambda: ReportArchiveConfig())
report_templates_dir: str = "templates/reports"
class ColumnMappingConfig(_StrictModel):
"""列映射配置模型
定义Excel列名到数据字段的映射关系,支持直接映射和别名映射.
Attributes:
direct: 直接列映射,键为数据字段名,值为Excel列名
aliases: 列别名映射,键为Excel列名,值为可能的别名列表
"""
direct: dict[str, str] = Field(default_factory=dict)
aliases: dict[str, list[str]] = Field(default_factory=dict)
class ValidRowCheck(_StrictModel):
"""有效行检查配置模型
定义用于判断Excel行是否为有效数据的检查规则.
Attributes:
enabled: 是否启用有效行检查,默认为True
key_fields: 用于判断行有效性的关键字段列表
"""
enabled: bool = True
key_fields: list[str] = Field(default_factory=list)
[文档]
class ExcelReadConfig(_StrictModel):
"""Excel 读取配置模型
定义 Excel 文件读取时的基本参数.
Attributes:
sheet_name: 工作表索引或名称,默认为 0
header_row: 表头行号(0-indexed),默认为 1
skip_rows: 跳过的行数,None 表示不跳过
use_cols: 使用的列范围,None 表示全部
"""
sheet_name: str | int = 0
header_row: int = 1
skip_rows: int | None = None
use_cols: str | None = None
class PnConvertConfig(_StrictModel):
"""公称压力 Lb→MPa 换算配置(#30 P0 DN/PN 字典 DB 化)
替代 VBA 型号压力转换表的 Lb→MPa 硬规则(150Lb=2 / 300Lb=5 / ...),
全部 yaml 化,后期只改配置不搜改代码。键为 LB 磅级(字符串整数),值为 MPa。
"""
lb_to_mpa: dict[str, float] = Field(
default_factory=lambda: {
"150": 2.0,
"300": 5.0,
"600": 10.0,
"800": 13.0,
"900": 15.0,
"1500": 25.0,
"2500": 42.0,
}
)
keep_as_string: list[str] = Field(default_factory=lambda: ["2.5Kpa", "-----", "/", "*Lb"])
class ImportGateConfig(_StrictModel):
"""导入门控配置(#30 P1 导入门控,yaml 驱动跳过/隔离无单号行)
替代 VBA「要货单号='假'/空 → 整行跳过」逻辑,全配置驱动:
- enabled: 总开关
- order_identity_fields: 现代要货单号来源字段(2025+ 行凭此逃逸门控,如
["sales_order_no", "production_order_no"])
- legacy_field: legacy 要货单号来源字段(pre-2025 行凭 plan_no(计划单号4位)
逃逸门控,VBA 即依 计划单号+计划日期年月 派生要货单号)
- skip_when_missing: 兼容旧名(=order_identity_fields 别名)
- mode: skip=直接丢弃 / isolate=隔离到待复核(返回 gated 列表)/ warn=仍导入但标黄
- match_keys: 门控匹配维度(隔离时用于与既有行归并的键,预留)
门控语义(业务正确):仅当「全部订单身份字段(含 legacy_field)均缺失」时,
该行才视为「无单号」无效行被门控;任一身份字段非空即逃逸(不误杀 pre-2025 行)。
"""
enabled: bool = False
order_identity_fields: list[str] = Field(default_factory=list)
legacy_field: str = ""
skip_when_missing: list[str] = Field(default_factory=list) # 兼容别名
mode: str = "isolate" # skip | isolate | warn
match_keys: list[str] = Field(default_factory=list)
[文档]
class DualFieldMappingConfig(_StrictModel):
"""双字段映射配置(合同信息 ↔ 合格证打印信息),蓝图 §4.2.1。
Attributes:
pairs: contract→cert 字段对列表,每项 {contract, cert}
import_mode: copy / override / lazy(默认 lazy,不破坏既有行为)
- copy:合同值同时写入 cert_*(杜绝"改合同静默跟随"风险)
- override:仅当 Excel 显式提供 cert 列时才写 cert_*
- lazy:保持现状(cert_* 留空,打印时回退合同值)
"""
pairs: list[dict[str, str]] = Field(default_factory=list)
import_mode: str = "lazy"
[文档]
class SalesPlanConfig(_StrictModel):
"""销售计划配置模型
定义销售计划Excel文件的解析规则,包括列映射、表头位置等.
Attributes:
excel_config: Excel 读取配置
column_mapping: 列映射配置
header_detection: 表头自动检测配置
styled_import_columns: 带格式导入时保留的列名列表
header_row: 表头所在行号(从1开始),默认为2
data_start_row: 数据起始行号(从1开始),默认为3
required_fields: 必填字段列表
valid_row_check: 有效行检查配置
pn_convert: 公称压力 Lb→MPa 换算(#30 P0 DN/PN 字典 DB 化)
import_gate: 导入门控(#30 P1 无单号行跳过/隔离/标黄)
dual_field_mapping: 双字段映射(合同信息 ↔ 合格证打印信息),蓝图 §4.2.1
"""
# §15.3 阶段3:严格校验
model_config = {"extra": "forbid"}
excel_config: ExcelReadConfig = Field(default_factory=ExcelReadConfig)
column_mapping: ColumnMappingConfig = Field(default_factory=ColumnMappingConfig)
header_detection: HeaderDetectionConfig | None = None
styled_import_columns: list[str] = Field(default_factory=list)
header_row: int = 2
data_start_row: int = 3
required_fields: list[str] = Field(default_factory=list)
valid_row_check: ValidRowCheck = Field(default_factory=ValidRowCheck)
pn_convert: PnConvertConfig = Field(default_factory=PnConvertConfig)
import_gate: ImportGateConfig = Field(default_factory=ImportGateConfig)
# 双字段映射(合同信息 ↔ 合格证打印信息),蓝图 §4.2.1
dual_field_mapping: DualFieldMappingConfig = Field(default_factory=DualFieldMappingConfig)
[文档]
class SuffixRule(_StrictModel):
"""合格证后缀规则模型
根据关键词匹配规则生成证书编号后缀.
Attributes:
field: 可选,指定匹配的字段名
keywords: 触发该后缀的关键词列表
suffix: 匹配成功时使用的后缀字符串
exclude: 可选,排除的关键词列表,匹配时不生成后缀
"""
field: str | None = None
keywords: list[str]
suffix: str
exclude: list[str] | None = None
class CertificateModeRule(_StrictModel):
"""编码模式判定规则(步骤 6.2)
按 when_field 字段值命中 keywords 中任一关键词 → 采用 mode(number_model).
Attributes:
when_field: 参与判定的字段名(如 customer);兼容旧键 field
field: when_field 的兼容别名
keywords: 触发该模式的关键词列表
mode: 命中时采用的编码模式数值(复用 printer.numbering_modes)
sn_kks_position: 双编码位置约定(仅 mode 5/6 双编码有意义,对齐 VBA
ar(2,8) 单元格),取值为 "B12单元格为产品编号,B13单元格为KKS编码"
或 "B12单元格为KKS编码,B13单元格为产品编号";空串=默认不互换。
"""
when_field: str | None = None
field: str | None = None
keywords: list[str] = Field(default_factory=list)
mode: int = 0
sn_kks_position: str = ""
class CertificateNumberingConfig(_StrictModel):
"""合格证编号时机与编码模式配置(步骤 6:蓝图 §4.4)
Attributes:
timing: 编号时机,on_demand(默认)/ on_import
range_regex: 范围编码解析正则,空则用内置默认
default_mode: 未命中任何 mode 规则时的默认编码模式
modes: 编码模式判定规则列表
"""
timing: str = "on_demand"
range_regex: str = ""
default_mode: int = 0
modes: list[CertificateModeRule] = Field(default_factory=list)
# 自动续号(默认 True):打印视图编号组在「目标年月」变化时,自动把「起始」
# 填为该月已用最大流水 + 1;关闭则默认手动填写(起始流水手填覆盖)。
auto_continue: bool = True
# 分隔符双约定(§5.7.2-B / 维度5):编号阶段用 separator_stage(默认 "---"),
# 保存清单/写证用 separator_list(默认 "-")。对齐 VBA:E2 填 G 列用 "-",
# 模块20 批量打印生成 "---" 并在写清单时替换为 "-"。
separator_stage: str = "---"
separator_list: str = "-"
# 前缀(编号年月)来源(§5.7.2-G / 维度15):
# now = datetime.now() 实时年月(默认,现状语义)
# plan_date = 取 SalePlan.plan_date 的年月(=计划年月)
# shipped_date = 取 SalePlan.shipped_date 的年月(=生产发货年月,最贴近 VBA 语义)
# 注:manual 由 prefix_override 显式覆盖,不在此枚举。
prefix_source: str = "now"
# 军工三位流水前缀(§5.7.2-D / 维度13,mode:7):无 V+YYMM 的军工前缀,
# 自动编号时用作三位流水前缀(如 "JG" → JG001A)。手动编号由调用方传 prefix。
military_prefix: str = "JG"
# 自动计算口径压力开关(§5.7.2-E / 维度8,对齐 VBA 模块工作表 G11):
# true = 打印前自动从型号解析口径(DN)/压力(PN) 并预填(现状,默认);
# false = 关闭自动推算,仅用已存值、不就口径/压力缺失弹补充对话框。
# 默认 true 保持与既有行为零差异。
auto_calc_dn_pn: bool = True
class InspectorIdConfig(_StrictModel):
"""检验工号默认配置(配置驱动,BUG-006 I / H3)
Attributes:
enabled: 是否启用自动填充检验工号(默认 True)
defaults: 按模板语言族映射默认工号(如 全中文→检5,其余→No 5)
fallback: 未匹配语言族时的默认工号
"""
# §15.3 阶段3:严格校验
model_config = {"extra": "forbid"}
enabled: bool = True
defaults: dict[str, str] = Field(default_factory=dict)
fallback: str = "检5"
class PrintDuplicateCheckConfig(_StrictModel):
"""打印存库重复检测配置(§5.7.2-C / 维度11,对齐 VBA「大连大高重复跳过存库」)。
Attributes:
enabled: 是否启用检测(默认 False → 零差异,所有客户照常落库)
customers: 触发去重的客户关键词列表(DingHuoDanWei 命中其一即检测)
compare_fields: 比对的 PrintLog 字段集(匹配数 = 字段值相等个数)
threshold: 最小匹配字段数阈值(≥ 此值判为重复,跳过落库)
"""
# §15.3 阶段3:严格校验
model_config = {"extra": "forbid"}
enabled: bool = False
customers: list[str] = Field(default_factory=list)
compare_fields: list[str] = Field(default_factory=list)
threshold: int = 0
[文档]
class CertificateConfig(_StrictModel):
"""合格证配置模型
定义合格证编号的生成规则.
Attributes:
prefix: 证书编号前缀,默认为"CERT"
date_format: 日期格式字符串,默认为"%Y%m%d"
format_template: 编号格式模板,支持{prefix}、{date}、{seq}占位符
suffix_rules: 后缀匹配规则列表
defaults: 默认值字典
numbering: 编号时机与编码模式配置(步骤 6)
"""
# §15.3 阶段3:严格校验
model_config = {"extra": "forbid"}
prefix: str = "CERT"
date_format: str = "%Y%m%d"
format_template: str = "{prefix}{date}{seq:04d}"
suffix_rules: list[SuffixRule] = Field(default_factory=list)
defaults: dict[str, Any] = Field(default_factory=dict)
numbering: CertificateNumberingConfig = Field(default_factory=CertificateNumberingConfig)
# 出厂日期年月来源(BUG-006 J,配置驱动):
# sync_numbering_ym(默认)= 同步产品编号年月;current_ym = 取当前年月
issue_date_source: str = "sync_numbering_ym"
# 检验工号默认(配置驱动,默认启用)
inspector_id: InspectorIdConfig = Field(default_factory=InspectorIdConfig)
# 打印存库重复检测(§5.7.2-C / 维度11,默认关闭 → 零差异)
print_duplicate_check: PrintDuplicateCheckConfig = Field(
default_factory=PrintDuplicateCheckConfig
)
[文档]
class ImportDeduplicationConfig(_StrictModel):
"""导入去重配置模型
定义数据导入时的重复检测和处理策略.
Attributes:
strategy: 去重策略,"skip"表示跳过重复项,"overwrite"表示覆盖
check_within_file: 是否检查文件内重复,默认为True
check_against_db: 是否检查与数据库重复,默认为True
unique_key_fields: 用于判断唯一性的字段列表
update_on_duplicate: 重复时是否检测变更并更新,默认为True
monitored_fields: 监控变更的字段列表
duplicate_handling: 重复处理扩展配置(V2)
unique_key_generation: 唯一键生成配置(V2)
field_normalization: 字段规范化配置(V2)
"""
# §15.3 阶段3:严格校验——未建模键将在启动期报 ValidationError,防止 on_collision 类静默失效复现
model_config = {"extra": "forbid"}
strategy: str = "skip"
check_within_file: bool = True
check_against_db: bool = True
unique_key_fields: list[str] = Field(default_factory=list)
update_on_duplicate: bool = True
monitored_fields: list[str] = Field(default_factory=list)
# 批内撞键(同 unique_key)处理策略:quarantine(隔离,默认) | merge_max(取大数量) | merge_sum(数量求和)
# 建模后由 cfg("import_deduplication.on_collision") 读取,修复此前被 extra=ignore 静默丢弃的失效配置
on_collision: str = "quarantine"
# V2 新增: 扩展去重配置
duplicate_handling: dict[str, Any] | None = None
unique_key_generation: dict[str, Any] | None = None
field_normalization: dict[str, Any] | None = None
[文档]
class DatabaseConfig(_StrictModel):
"""数据库配置模型
定义数据库连接参数和行为.
Attributes:
type: 数据库类型,默认为"sqlite"
echo: 是否打印SQL语句日志,默认为False
pool_size: 数据库连接池大小,默认为5
max_overflow: 连接池最大溢出连接数,默认为10
"""
# §15.3 阶段3:严格校验
model_config = {"extra": "forbid"}
type: str = "sqlite"
echo: bool = False
pool_size: int = 5
max_overflow: int = 10
[文档]
class LoggingConfig(_StrictModel):
"""日志配置模型
定义日志输出格式、轮转策略等参数.
Attributes:
level: 日志级别,可选值为DEBUG、INFO、WARNING、ERROR、CRITICAL
file: 日志文件路径,默认为"logs/certflow.log"
format: 日志格式字符串
rotation: 日志文件轮转条件,如"10 MB"表示10MB轮转
retention: 日志保留时间,如"30 days"表示保留30天
compression: 日志压缩格式,默认为"zip"
debug_modules: DEBUG 黑名单列表,支持模块名或"模块名:函数名"格式
"""
# §15.3 阶段3:严格校验(收紧:此前为 allow,探测确认无未建模子键)
model_config = {"extra": "forbid"}
level: str = "INFO"
file: str = "logs/certflow.log"
format: str = "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {name}:{function}:{line} - {message}"
rotation: str = "10 MB"
retention: str = "30 days"
compression: str = "zip"
debug_modules: list[str] = Field(default_factory=list)
class WindowConfig(_StrictModel):
"""窗口配置模型
定义应用程序主窗口的外观和行为.
Attributes:
title: 窗口标题,默认为"CertFlow - 证书管理系统"
min_width: 窗口最小宽度,默认为1000像素
min_height: 窗口最小高度,默认为618像素
default_width: 窗口默认宽度,默认为1200像素
default_height: 窗口默认高度,默认为800像素
"""
title: str = "CertFlow - 证书管理系统"
min_width: int = 1000
min_height: int = 618
default_width: int = 1200
default_height: int = 800
class TableConfig(_StrictModel):
"""表格配置模型
定义数据表格的显示行为.
Attributes:
alternating_row_colors: 是否使用交替行颜色,默认为True
sorting_enabled: 是否启用排序功能,默认为True
stretch_last_section: 是否拉伸最后一列,默认为True
"""
alternating_row_colors: bool = True
sorting_enabled: bool = True
stretch_last_section: bool = True
class StatusBarConfig(_StrictModel):
"""状态栏配置模型
定义状态栏的显示内容.
Attributes:
ready_message: 就绪状态显示的消息,默认为"✅ 系统就绪"
"""
ready_message: str = "✅ 系统就绪"
[文档]
class UIConfig(_StrictModel):
"""UI配置集合模型
聚合所有用户界面相关的配置.
Attributes:
window: 窗口配置
table: 表格配置
status_bar: 状态栏配置
query_page: 查询页面配置(视图模板、操作符、字段映射等)
"""
# §15.3 阶段3:UI 段经 ui.yaml !include 并入大量未建模顶层键(main_window/title_bar/
# nav_buttons/log_page/print_page/styles/welcome_page 等,见审计报告 §3),UIConfig 作为
# 该段兜底容器须保持 allow,不能开 forbid,否则整体加载崩溃。
model_config = {"extra": "allow"}
window: WindowConfig = Field(default_factory=WindowConfig)
table: TableConfig = Field(default_factory=TableConfig)
status_bar: StatusBarConfig = Field(default_factory=StatusBarConfig)
query_page: dict = Field(default_factory=dict)
[文档]
class ColorMappingConfig(_StrictModel):
"""颜色映射配置模型
定义状态和优先级到颜色的映射关系.
Attributes:
status_colors: 状态到颜色的映射字典
priority_colors: 优先级到颜色的映射字典
vba_colors: VBA颜色索引到颜色的映射(V2)
font_color_to_shipping: 字体颜色到发货状态的映射(V2)
bg_color_to_status: 背景颜色到状态的映射(V2)
theme_font_colors: THEME 字体颜色到状态的映射(V3)
font_colors: 字体颜色 hex 字符串映射
bg_colors: 背景颜色 hex 字符串映射
"""
# §15.3 阶段3:严格校验
model_config = {"extra": "forbid"}
status_colors: dict[str, str] = Field(default_factory=dict)
priority_colors: dict[str, str] = Field(default_factory=dict)
# V2 新增: VBA 颜色映射
vba_colors: dict[int, str] | None = None
font_color_to_shipping: dict[int, str] | None = None
bg_color_to_status: dict[int, str] | None = None
# V3 新增: THEME 颜色和 hex 颜色映射
theme_font_colors: dict[str, str] | None = None
font_colors: dict[str, str] | None = None
bg_colors: dict[str, str] | None = None
# ============================================================
# V2 新增配置模型
# ============================================================
[文档]
class NumberingSuffixRule(_StrictModel):
"""编号后缀规则模型(V2新增)
根据字段值或关键词匹配规则生成编号后缀.
Attributes:
field: 用于匹配的字段名
keywords: 触发该后缀的关键词列表
suffix: 匹配成功时使用的后缀字符串
exclude: 排除的关键词列表,匹配时不生成后缀
"""
field: str | None = None
keywords: list[str] = Field(default_factory=list)
suffix: str = ""
exclude: list[str] | None = None
[文档]
class NumberingConfig(_StrictModel):
"""编号规则配置模型(V2新增)
定义V2版本的自动编号生成规则.
Attributes:
prefix: 编号前缀,默认为"V"
format: 编号格式模板
suffixes: 后缀映射字典
auto_number_mode: 自动编号模式配置
suffix_rules: 后缀规则列表
no_cert_categories: 免编号品类关键词(螺栓/密封圈等免出证铭牌件)
yellow_skip_enabled: 是否按源表黄底(65535)跳过编号(默认 False)
"""
prefix: str = "V"
format: str = "{prefix}{year}{month}{seq:03d}{suffix}"
suffixes: dict[str, str] = Field(default_factory=dict)
auto_number_mode: dict[str, Any] | None = None
suffix_rules: list[NumberingSuffixRule] = Field(default_factory=list)
# 维度16/H:对齐 VBA 黄底跳过语义——导入期命中免编号品类的记录
# needs_numbering=False,编号(auto/manual)时跳过以压低流水位数。
no_cert_categories: list[str] = Field(default_factory=list)
yellow_skip_enabled: bool = False
[文档]
class DuplicateCheckConfig(_StrictModel):
"""重复检查配置模型(V2新增)
定义数据重复检查的字段列表.
Attributes:
fields: 用于重复检查的字段名列表
"""
fields: list[str] = Field(default_factory=list)
[文档]
class UniqueKeyField(_StrictModel):
"""唯一键字段模型(V2新增)
定义构成唯一键的字段及其约束.
Attributes:
name: 字段名称
required: 该字段是否为必填,默认为False
"""
name: str
required: bool = False
class UniqueKeyTier(_StrictModel):
"""去重身份键分层配置(§9 落地待办①)
每一层描述一组用于生成去重身份键的字段。modern_2025 用于 2025+ 双号齐全,
legacy 用于 pre-2025 降级。字段集不含 quantity(分批发货靠 _detect_changes 取大合并)。
"""
description: str = ""
fields: list[str] = Field(default_factory=list)
[文档]
class UniqueKeyConfig(_StrictModel):
"""唯一标识规则配置模型(V2新增)
定义唯一键的生成规则.
Attributes:
separator: 字段值连接符,默认为"|"
date_to_serial: 是否将日期转换为序列号,默认为True
fields: 构成唯一键的字段列表(V2 旧格式,兼容保留)
key_field_fallbacks: 唯一键字段的回退映射
tiers: 去重身份键分层字段(§9 落地待办①,单一真相源)
require_numbered: 仅匹配已编号行才判重(565 行语义,§9 待办①)
"""
separator: str = "|"
date_to_serial: bool = True
fields: list[UniqueKeyField] = Field(default_factory=list)
key_field_fallbacks: dict[str, list[str]] | None = None
tiers: dict[str, UniqueKeyTier] | None = None
require_numbered: bool = False
[文档]
class GroupingNumberingConfig(_StrictModel):
"""分组序号配置"""
use_global_prefix: bool = True
prefix_format: str = "G{:03d}"
seq_format: str = "{:03d}"
separator: str = "-"
full_seq_field: str = "group_seq"
group_index_field: str = "_group_index"
group_key_field: str = "_group_key"
[文档]
class GroupingConfig(_StrictModel):
"""分组配置(V3新增)
第一层分组:按业务键分组,生成组内序号
"""
group_keys: list[str] = Field(default_factory=list)
group_keys_no_project: list[str] = Field(default_factory=list)
numbering: GroupingNumberingConfig = Field(default_factory=GroupingNumberingConfig)
[文档]
class SortingKey(_StrictModel):
"""排序键配置(V3新增)"""
field: str
order: str = "asc"
[文档]
class SortingConfig(_StrictModel):
"""排序配置(V3新增)
第二层排序:组内按产品键排序
"""
keys: list[SortingKey] = Field(default_factory=list)
[文档]
class PriorityRulesConfig(_StrictModel):
"""优先级规则配置模型(V2新增)
定义基于金额的优先级判定规则和颜色.
Attributes:
high_threshold: 高优先级阈值,金额大于此值为高优先级,默认为10000
medium_threshold: 中优先级阈值,金额大于此值为中优先级,默认为5000
high_label: 高优先级标签,默认为"高"
medium_label: 中优先级标签,默认为"中"
low_label: 低优先级标签,默认为"低"
high_color: 高优先级颜色(十六进制),默认为"#e74c3c"
medium_color: 中优先级颜色(十六进制),默认为"#f39c12"
low_color: 低优先级颜色(十六进制),默认为"#27ae60"
"""
high_threshold: int = 10000
medium_threshold: int = 5000
high_label: str = "高"
medium_label: str = "中"
low_label: str = "低"
high_color: str = "#e74c3c"
medium_color: str = "#f39c12"
low_color: str = "#27ae60"
class ExportAccessConfig(_StrictModel):
"""Access 导出开关配置
Attributes:
enabled: 是否启用 Access 导出(需安装 pyodbc + Access ODBC 驱动)
"""
# §15.3 阶段3:严格校验
model_config = {"extra": "forbid"}
enabled: bool = False
[文档]
class ExportConfig(_StrictModel):
"""导出配置模型(V2新增)
定义数据导出的格式和行为.
Attributes:
hyperlink_column: 超链接所在列号(从1开始),默认为44
hyperlink_ref_prefix: 超链接引用前缀,默认为"AR"
hyperlink_width: 超链接列宽度,默认为20字符
summary: 汇总导出配置
access: Access 导出开关(export.access.enabled)
"""
# §15.3 阶段3:严格校验——未建模键将在启动期报 ValidationError,防止 export.access 类静默失效复现
model_config = {"extra": "forbid"}
hyperlink_column: int = 44
hyperlink_ref_prefix: str = "AR"
hyperlink_width: int = 20
summary: dict[str, Any] | None = None
# 建模后由 cfg("export.access.enabled") 读取,修复此前被 extra=ignore 静默丢弃导致 Access 导出无法开启的失效配置
access: ExportAccessConfig | None = None
class FieldFallbackConfig(_StrictModel):
"""字段回退链配置模型(V2新增)
占位模型,实际使用Dict[str, List[str]]表示字段回退关系.
"""
pass
[文档]
class ImportMappingConfig(_StrictModel):
"""数据导入列映射配置模型(V2新增)
定义Excel导入时的预处理、列映射和默认值.
Attributes:
preprocessor: 预处理器配置
column_mapping: 列映射规则列表
default_values: 默认值映射
"""
preprocessor: dict[str, Any] | None = None
column_mapping: list[dict[str, Any]] | None = None
default_values: dict[str, str] | None = None
[文档]
class SalesImporterConfig(_StrictModel):
"""销售计划导入器配置模型(V2新增)
定义销售计划Excel导入的字段映射和颜色列配置.
Attributes:
field_mapping: 字段映射字典,键为Excel列名,值为数据字段名
color_columns: 颜色列配置
selection_info_columns: 选择信息列列表
"""
field_mapping: dict[str, str] = Field(default_factory=dict)
color_columns: dict[str, Any] | None = None
selection_info_columns: list[str] | None = None
[文档]
class VBAImportConfig(_StrictModel):
"""VBA导入逻辑配置模型(V2新增)
定义VBA导入功能的文件路径、字段映射和重复检查规则.
Attributes:
sales_plan_files: 销售计划文件路径映射
qualified_list: 合格清单文件路径映射
field_mapping: 字段映射字典
color_mapping: 颜色映射配置
duplicate_check_fields: 重复检查字段列表
"""
sales_plan_files: dict[str, str] | None = None
qualified_list: dict[str, str] | None = None
field_mapping: dict[str, str] = Field(default_factory=dict)
color_mapping: dict[str, Any] | None = None
duplicate_check_fields: list[str] | None = None
[文档]
class PrintLayoutConfig(_StrictModel):
"""打印布局配置模型
定义打印坐标、偏移和字体大小.
Attributes:
label_x: 标签 X 坐标
value_x: 值 X 坐标
start_y: 起始 Y 坐标
line_spacing: 行间距
font_size: 字体大小
x_offset: 打印机物理偏移修正 (mm)
y_offset: 打印机物理偏移修正 (mm)
"""
label_x: float = 3
value_x: float = 20
start_y: float = 22
line_spacing: float = 6.5
font_size: float = 9
x_offset: float = 14
y_offset: float = 0
[文档]
class PrintFontConfig(_StrictModel):
"""打印字体配置模型
Attributes:
name: 字体名称
weight: 字体粗细
"""
name: str = "SimSun"
weight: int = 400
[文档]
class PrintFieldConfig(_StrictModel):
"""打印字段配置模型
Attributes:
label: 字段标签
key: 字段键名
offset: 偏移量
"""
label: str
key: str
offset: int = 0
[文档]
class TestReportConfig(_StrictModel):
"""试压报告配置模型(V2新增)
定义试压报告的相关配置.
Attributes:
company_name: 公司名称
form_code: 表单代码
"""
company_name: str = ""
form_code: str = ""
[文档]
class PrinterConfig(_StrictModel):
"""打印机配置模型(V2新增)
定义默认打印机设置.
Attributes:
default: 默认打印机名称
"""
default: str = ""
[文档]
class EnvironmentConfig(_StrictModel):
"""环境信息配置模型(V2新增)
定义运行时环境信息,支持环境变量替换.
Attributes:
computer_name: 计算机名称,支持${COMPUTERNAME:Unknown}语法
username: 用户名,支持${USERNAME:Unknown}语法
"""
computer_name: str = "${COMPUTERNAME:Unknown}"
username: str = "${USERNAME:Unknown}"
[文档]
class ColorsConfig(_StrictModel):
"""颜色常量值配置模型(V2新增)
定义VBA中使用的颜色常量值.
Attributes:
yellow: 黄色的RGB整数值,默认为65535
blue: 蓝色的RGB整数值,默认为16711680
red: 红色的RGB整数值,默认为255
green: 绿色的RGB整数值,默认为65280
light_blue: 浅蓝色的RGB整数值,默认为15773696
"""
yellow: int = 65535
blue: int = 16711680
red: int = 255
green: int = 65280
light_blue: int = 15773696
[文档]
class CompletedOrdersBackfillConfig(_StrictModel):
"""已完成工单回填配置模型
定义从"已完成工单"Excel的月份工作表中读取"供货类型"字段并回填到数据库的规则。
核心设计:
- 只处理月份工作表(2026-01~2026-12)
- 按字段名称(列标题)匹配,不依赖列号/列字母
- 通过 production_order_no 匹配(同一生产令号所有记录回填相同值)
"""
file_path: str = "temp/excel/已完成工单【202601--202612】.xlsx"
match_field: str = "生产令号" # Excel 中用于匹配的列名
source_field: str = "供货类型" # Excel 中要读取的源字段列名
db_match_field: str = "production_order_no" # DB 匹配字段
db_target_field: str = "supply_type" # DB 目标字段
overwrite_existing: bool = False
backup_before_update: bool = True
backup_suffix: str = "_before_supply_type_backfill"
# ============================================================
# 生产状态配置模型
# ============================================================
[文档]
class ProductionStatusUILabel(_StrictModel):
"""生产状态 UI 标签模型"""
icon: str = ""
label: str = ""
[文档]
class ProductionStatusAutoUpgradeRule(_StrictModel):
"""自动升级规则模型
定义文档完成后自动升级生产状态的规则,按顺序匹配,命中即停止.
condition 是 Python 表达式,可用变量: progress_percent, is_ready_to_ship, production_status.
"""
condition: str
set_to: str | None = None
set_all_docs_done: bool | None = None
[文档]
class ProductionStatusInferenceRule(_StrictModel):
"""状态推断规则模型
从 execution_status 自由文本中通过正则匹配推断生产状态.
"""
patterns: list[str]
set_to: str
[文档]
class ProductionStatusUIConfig(_StrictModel):
"""生产状态 UI 显示配置"""
show_raw_execution_tooltip: bool = True
show_icon_for_inferred: bool = True
inferred_icon: str = "📋"
[文档]
class ProductionStatusConfig(_StrictModel):
"""生产状态配置模型
所有状态值、图标、流转规则、自动升级规则均由此模型定义,
新增状态只需修改 config.yaml,无需改动任何代码.
"""
# §15.3 阶段3:严格校验——未建模键将在启动期报 ValidationError,防止 shipped_actions 类静默失效复现
model_config = {"extra": "forbid"}
values: list[str] = Field(default_factory=list)
default: str = "待生产"
ui_labels: dict[str, ProductionStatusUILabel] | None = None
transitions: dict[str, list[str]] | None = None
auto_upgrade: list[ProductionStatusAutoUpgradeRule] | None = None
inference_rules: list[ProductionStatusInferenceRule] | None = None
query_keywords: dict[str, str] | None = None
ui: ProductionStatusUIConfig | None = None
context_menu: ProductionStatusContextMenu | None = None
# 已发货处理(三层数据生命周期)的归档/导出配置,由 ui_config 经
# _pscfg("production_status.shipped_actions") 读取;建模后修复此前被 extra=ignore
# 静默丢弃导致「已发货」菜单回退硬编码默认值的问题
shipped_actions: dict[str, Any] | None = None
# ============================================================
# ID 生成器配置模型
# ============================================================
[文档]
class IdGeneratorConfig(_StrictModel):
"""唯一键生成策略配置模型
定义用于生成数据记录唯一键的字段组合和空值标记前缀.
"""
empty_marker_prefix: str = "【空白"
new_data_fields: list[str] = Field(default_factory=list)
old_data_fields: list[str] = Field(default_factory=list)
# ============================================================
# 导入配置模型
# ============================================================
[文档]
class ImportColumnConfig(_StrictModel):
"""导入列配置模型"""
field: str
label: str
type: str | None = None
required: bool = False
[文档]
class ImportViewConfig(_StrictModel):
"""导入视图配置模型"""
name: str = ""
icon: str = ""
required_fields: list[str] = Field(default_factory=list)
columns: list[ImportColumnConfig] | None = None
[文档]
class ImportConfig(_StrictModel):
"""导入配置模型(与 QueryView 风格一致)"""
default_strategy: str = "smart"
auto_save: bool = True
views: dict[str, ImportViewConfig] | None = None
[文档]
class ImportPlaceholderConfig(_StrictModel):
"""导入期占位目录配置(B0-4 — 批次0 B-CreateDIR/F 已定稿)
决策(2026-07-14):测试期整表批量导入会在测试机泛滥生成大量空订单目录,
故改为配置驱动开关、默认关闭。
Attributes:
create_placeholder_dirs: 是否恢复 VBA 式导入期按订单组预建占位项目文件夹。
False(默认):导入只写 SQLite,绝不碰磁盘;下游写文件时由写端
os.makedirs(parents=True, exist_ok=True) 惰性建目录。
True:导入期按订单组(计划日期+订货单位+项目名称)预建空项目资料文件夹,
命名统一沿用 paths.report_archive.folder 的 pattern/tokens/兜底文本。
"""
# §15.3 阶段3:严格校验——未建模键将在启动期报 ValidationError,防止 skip_confirm_when_no_conflict 类静默失效复现
model_config = {"extra": "forbid"}
create_placeholder_dirs: bool = False
# 0 冲突/0 隔离时跳过确认对话框,直接导入并弹一次完成提示(蓝图 §4.1.3)
# 建模后由 cfg("import.skip_confirm_when_no_conflict") 读取,修复此前被 extra=ignore 静默丢弃的失效配置
skip_confirm_when_no_conflict: bool = True
class BomImportConfig(_StrictModel):
"""BOM 导入控件配置(配置驱动 UI 初始态)
对应 ``BomImportWidget``(计划导入页的「BOM导入(目录扫描)」标签页)。
Attributes:
default_folder: 材料清单目录默认值(绝对路径,或含 ``${ENV:default}``)。
留空则 UI 不预填,用户须点「浏览」选择。支持环境变量替换。
default_mode: 默认导入模式,决定 UI 打开时预选的单选按钮,取值须为
``clear`` / ``append`` / ``refresh`` / ``skip`` 之一。
默认 ``skip``(增量跳过已导入,最安全最快,适合日常只扫新文件);
需要重建全表时再改为 ``clear`` 或在 UI 手动切换。
"""
model_config = {"extra": "forbid"}
default_folder: str = ""
default_mode: str = "skip"
# ============================================================
# 打印引擎配置模型
# ============================================================
[文档]
class PrintEscpConfig(_StrictModel):
"""ESC/P-K 打印配置"""
x_offset: float = 14
y_offset: float = 0
class PrintBatchConfig(_StrictModel):
"""批量打印配置(原 certificate_print.batch 迁移至 print.batch,见 §16.3)"""
batch_size: int = 100
delay_seconds: float = 2
preview_delay_seconds: float = 10
first_direct_print: bool = True
[文档]
class PrintConfig(_StrictModel):
"""打印引擎配置模型(合并后)
注意:与 ``certflow.config.print_config.RuntimePrintConfig``(运行时聚合类)同名,
本类为 pydantic 引擎配置模型,仅描述 ``print`` 段结构。
"""
engine: str = "escp"
default_printer: str = "EPSON LQ-635KII ESC/P2"
default_copies: int = 1
# 默认打印模板(设置页可改;显示标签需匹配 PrintView 模板下拉项,如"全中文合格证模板")
default_template: str = "全中文合格证模板"
escp: PrintEscpConfig | None = None
# 🆕 打印版式(canonical,原 certificate_print.paper/layout/font 迁移至此,见 §16.3)
paper: dict[str, float] | None = None
layout: PrintLayoutConfig | None = None
font: PrintFontConfig | None = None
# 🆕 批量打印参数(原 certificate_print.batch 迁移至此)
batch: PrintBatchConfig | None = None
# 🆕 打印字段定义(原 certificate_print.print_fields 迁移至此,cert_print_engine 消费)
print_fields: list[PrintFieldConfig] | None = None
# 🆕 预览选项(预印合格证:可关标题只显示值)
preview: PrintPreviewConfig | None = None
# 🆕 打印防错护栏 + 字典自学习配置(见 docs/PRINT_BLUEPRINT.md)
guard: PrintGuardConfig | None = None
# 🆕 入队前 raw 字节抓取配置(调试/分析用,默认关闭,见 PRINT_BLUEPRINT.md §14)
raw_capture: PrintRawCaptureConfig | None = None
class PrintPreviewConfig(_StrictModel):
"""合格证预览选项(仅影响 GUI 预览渲染,不影响 ESC/P 实际打印)"""
show_labels: bool = True
class PrintRawCaptureConfig(_StrictModel):
"""入队前 raw 字节抓取配置(调试/分析用,默认关闭,见 PRINT_BLUEPRINT.md §14)
- enabled: 总开关,默认关;设 true 才在「入队」时抓取 ESC/P-K 原始字节。
- output_dir: 落盘目录(相对运行目录;不存在则自动创建)。
- save_json: 是否同时保存结构化 JSON(字段值+模板+编号)便于人工分析。
"""
enabled: bool = False
output_dir: str = "raw_print"
save_json: bool = True
# ============================================================
# 打印防错护栏 + 字典自学习配置(docs/PRINT_BLUEPRINT.md)
# print.guard 段必须在此建模,否则 PrintConfig(_StrictModel=forbid) 会丢弃该段
# ============================================================
class GuardRequiredFieldConfig(_StrictModel):
"""单个必填字段的校验配置(print.guard.required_fields[])"""
key: str
required: bool = True
reconfirm: bool = False # 即使有值也弹 InputBox 二次确认(如 temperature)
class GuardConfirmationConfig(_StrictModel):
"""确认闸口配置(print.guard.confirmation)"""
single_batch: bool = True # 单张/单计划批量也弹确认
multi_batch: bool = True # 多计划批量每批发前弹确认
summary_fields: list[str] = Field(default_factory=list)
class GuardLearnBackConfig(_StrictModel):
"""学回字典配置(print.guard.learn_back)"""
enabled: bool = True
strategy: str = "conservative" # conservative | last_write_wins | field_level
overwrite_manual: bool = False # manual 来源条目永不自动覆盖
fields: list[str] = Field(default_factory=list) # 仅学这 4 个模型级字段
class GuardAutofillInspectorDefaultConfig(_StrictModel):
"""检验工号空值兜底配置(print.guard.autofill.inspector_default)"""
chinese: str = "检5" # 中文合格证(全中文模板)默认检验工号
other: str = "No 5" # 其他合格证(中/俄/全英文模板)默认检验工号
class GuardAutofillConfig(_StrictModel):
"""加载自动带出配置(print.guard.autofill):表单字段 -> model_param_mappings 列"""
enabled: bool = True
fields: dict[str, str] = Field(default_factory=dict)
inspector_default: GuardAutofillInspectorDefaultConfig | None = None
class PrintGuardConfig(_StrictModel):
"""打印防错护栏总配置(print.guard)"""
autofill: GuardAutofillConfig = Field(default_factory=GuardAutofillConfig)
required_fields: list[GuardRequiredFieldConfig] = Field(default_factory=list)
confirmation: GuardConfirmationConfig = Field(default_factory=GuardConfirmationConfig)
learn_back: GuardLearnBackConfig = Field(default_factory=GuardLearnBackConfig)
# ⑤ 与历史差异校验闸(§11.3 决议):取同型号上次实际打印值比对,
# 字段在当前值 != 历史值时强制人眼确认(不只拦空值)。默认最全集。
reconfirm_on_diff: list[str] = Field(
default_factory=lambda: ["temperature", "medium", "check_standard", "pn", "dn"]
)
# ============================================================
# 统一输出配置(OutputView 配置驱动,见 docs/PRINT_BLUEPRINT.md §11.5)
# 打印/扫描/报告共用同一套护栏(shared.guard_ref)与历史回填兜底(shared.history_backfill)
# ============================================================
class ScanDualCodeConfig(_StrictModel):
"""扫描件双编码(SN 短码 / KKS 长码)渲染开关配置
默认关闭(普通用户扫描件不含 SN/KKS,与既有行为一致);仅特殊用户/项目场景
置 enabled: true 时,扫描件才会渲染双编码,字段由逐台单号派生(对齐打印侧
_derive_sn_and_kks),电厂件真 KKS 受保护优先采用 SalePlan.kks_code。
"""
enabled: bool = False
[文档]
class OutputTargetConfig(_StrictModel):
"""单个输出目标配置(output.targets.<name>)"""
engine: str = "escp" # 输出动作类型: escp | pil | report
controls: list[str] = Field(default_factory=list) # OutputView 动态渲染的控件集
run: str = "" # 输出动作入口的服务函数引用键(OutputView dispatch 据此调用)
# 输出动作前完整性闸门必检字段(替代旧 scan_view 硬编码 _SCAN_REQUIRED_FIELDS,配置驱动)
required_fields: list[str] = Field(default_factory=list)
# 控件默认值(配置驱动):ScanView/OutputView 启动时读取并应用;缺省走代码回退值。
# 支持键:template_type / format / rows(每列张数) / cols(每行张数) /
# paper_width_mm / paper_height_mm / output_dir。
defaults: dict[str, Any] = Field(default_factory=dict)
# 双编码(SN/KKS)渲染开关(仅扫描目标使用,默认关闭)
dual_code: ScanDualCodeConfig = Field(default_factory=ScanDualCodeConfig)
# 打印目标复选框默认态(配置驱动,PrintView 启动时读取;缺省走代码回退 True)
# 支持键:auto_fill / save_log / preview_labels(均为 bool)
options: dict[str, Any] = Field(default_factory=dict)
[文档]
class HistoryBackfillConfig(_StrictModel):
"""历史回填兜底配置(output.shared.history_backfill,§11.2)"""
enabled: bool = True
source: str = "print_logs" # 复用 PrintLog 作历史源(零新表)
key: list[str] = Field(default_factory=lambda: ["product_model", "product_spec"])
fallback_fields: list[str] = Field(
default_factory=lambda: ["temperature", "medium", "check_standard", "pn", "dn"]
)
[文档]
class OutputSharedConfig(_StrictModel):
"""统一输出共享配置(output.shared)"""
guard_ref: str = "print.guard" # 护栏复用 print.guard 段(不重复定义)
history_backfill: HistoryBackfillConfig = Field(default_factory=HistoryBackfillConfig)
class LayoutGroupConfig(_StrictModel):
"""常用布局配置驱动组(D 延后仅预留接口)。
每个组是一套效果集的命名组合(目标+模板+拼版等),后期 UI 用单选框/
命令按钮加载某组即一键套用整套效果;底层各配置控件底座照常保留。
"""
name: str = ""
enabled: bool = False
target: str = "" # 输出目标(print/scan)
template: str = "" # 模板族(全中文/中英文)
layout: str = "" # 拼版推荐值 label(对应 targets.scan.defaults.layout_presets)
[文档]
class OutputConfig(_StrictModel):
"""统一输出总配置(output.*,驱动 OutputView)"""
shared: OutputSharedConfig = Field(default_factory=OutputSharedConfig)
targets: dict[str, OutputTargetConfig] = Field(default_factory=dict)
default_target: str = "print"
layout_groups: list[LayoutGroupConfig] = Field(default_factory=list)
# ============================================================
# 同步配置模型
# ============================================================
[文档]
class SyncWebdavConfig(_StrictModel):
"""WebDAV 同步配置"""
url: str = ""
username: str = ""
password: str = ""
remote_path: str = "/certflow.db"
[文档]
class SyncConfig(_StrictModel):
"""数据库同步配置模型"""
webdav: SyncWebdavConfig | None = None
# ============================================================
# 底板预览配置模型
# ============================================================
[文档]
class BackgroundConfig(_StrictModel):
"""底板预览配置模型"""
show_in_preview: bool = True
opacity: float = 0.3
image_path: str = ""
# ============================================================
# 销售计划批量填充配置模型
# ============================================================
[文档]
class SalesPlanFillColumn(_StrictModel):
"""销售计划填充列配置"""
name: str
[文档]
class SalesPlanFillConfig(_StrictModel):
"""销售计划批量填充配置模型"""
columns: list[SalesPlanFillColumn] | None = None
# ============================================================
# 导出调试配置模型
# ============================================================
[文档]
class ExportDebugField(_StrictModel):
"""导出调试字段配置"""
field: str
use_fallback: bool = False
[文档]
class ExportDebugConfig(_StrictModel):
"""导出调试配置模型"""
fields: list[ExportDebugField] | None = None
# ============================================================
# 字段填充规则配置模型
# ============================================================
[文档]
class FieldFillRule(_StrictModel):
"""单条字段填充规则"""
source: str = "auto"
pattern: str = ""
transforms: dict[str, FieldFillTransform] | None = None
# ============================================================
# 打印日志空值默认标记配置模型
# ============================================================
[文档]
class PrintLogEmptyMarkersConfig(_StrictModel):
"""打印日志空值默认标记配置模型
对应 VBA 将打印内容添加到Access数据库() 中的 Br(1..51) 赋值.
"""
customer: str = "【空白订货单位】"
project_name: str = "【空白项目名称】"
product_name: str = "【空白产品名称】"
product_model: str = "【空白产品型号】"
dn: str = "空白公称通径"
pn: str = "空白公称压力"
medium: str = "【空白适用介质】"
temperature: str = "【空白适用温度】"
body_material: str = "【未录入阀体材质】"
stem_material: str = "【未录入阀杆材质】"
disc_material: str = "【未录入启闭件材质】"
tech_requirements: str = "【空白技术要求】"
equipment_no: str = "【空白设备位号】"
sub_project: str = "【空白子项目】"
sales_sn: str = "【空白销售部产品编号】"
category: str = "【空白销售部种类】"
weight: str = "【空白销售部重量要求】"
seq_no: str = "【空白序号】"
requisition_no: str = "【空白营销部要货单号】"
max_inlet_head: str = "【空白】"
max_boost_head: str = "【空白】"
# ============================================================================
# §15 严格校验收尾(A-C):4 个业务段未建模顶层键补模型
# 原属审计报告 §3 的 14 个未建模顶层键中的业务段(dictionary/lookup_tables/
# quarantine/sort_rules),此前经 !include 并入 RootConfig 顶层、靠 extra=allow
# 透传并被 cfg() 自由读取。现建模为子模型(继承 _StrictModel=forbid),
# 使其受严格校验保护,杜绝子键拼写错误/死配置静默失效。
# UI 段 12 个顶层键(main_window/title_bar/...)已于任务 2.3 建模为宽松字段并配合
# RootConfig 顶层 forbid 闭环,不再是 allow 例外。
# ============================================================================
class DictionaryModelParamMappingConfig(_StrictModel):
"""型号参数字典(model_param_mappings)的解析配置(仅描述数据位置与格式,不含数据本身)。"""
csv: str = "scripts/data/model_param_mappings.csv"
encoding: str = "utf-8-sig"
delimiter: str = ","
header: list[str] = Field(default_factory=list)
normalize_standard: bool = True
fallback_raw_data: bool = True
xlsx_source: str = "data/xlsx/型号压力对照.xlsx"
class DictionaryMaterialGradesConfig(_StrictModel):
"""材质牌号字典(material_grades)的解析配置(wf2 B2,DB 快照 CSV)。"""
csv: str = "scripts/data/material_grades.csv"
encoding: str = "utf-8-sig"
delimiter: str = ","
header: list[str] = Field(default_factory=list)
fallback_raw_data: bool = False
xlsx_source: str = "data/xlsx/材质数据源.xlsx"
class DictionaryCaliberMappingsConfig(_StrictModel):
"""口径映射字典(caliber_mappings)的解析配置(wf2 B3,RAW_DATA 外部化为 CSV)。"""
csv: str = "scripts/data/caliber_mappings.csv"
encoding: str = "utf-8-sig"
delimiter: str = ","
header: list[str] = Field(default_factory=list)
fallback_raw_data: bool = True
xlsx_source: str = ""
class DictionaryConfig(_StrictModel):
"""基础字典配置(dictionary.*,对应 dictionaries.yaml)。"""
model_param_mappings: DictionaryModelParamMappingConfig = Field(
default_factory=DictionaryModelParamMappingConfig
)
material_grades: DictionaryMaterialGradesConfig = Field(
default_factory=DictionaryMaterialGradesConfig
)
caliber_mappings: DictionaryCaliberMappingsConfig = Field(
default_factory=DictionaryCaliberMappingsConfig
)
class LookupTableContextMenuItemConfig(_StrictModel):
"""基础对照表右键菜单项。"""
action: str
table: str
label: str
class LookupTableContextMenuConfig(_StrictModel):
"""查询结果表「维护基础数据」子菜单配置(lookup_tables.context_menu)。"""
enabled: bool = True
label: str = ""
items: list[LookupTableContextMenuItemConfig] = Field(default_factory=list)
class LookupTableQuickFilterConfig(_StrictModel):
"""单条「快速筛选」按钮配置(lookup_tables.quick_filters.<table>[])。
点击即在筛选条追加一条预置条件;``value`` 可为字符串(如 ``"False"``)
或数值,``operator`` 默认 ``eq``。
"""
label: str = ""
field: str = ""
operator: str = "eq"
value: Any = None
class LookupTablesConfig(_StrictModel):
"""基础对照表驱动配置(lookup_tables.*,对应 quarantine.yaml 中的 lookup_tables 段)。"""
context_menu: LookupTableContextMenuConfig = Field(default_factory=LookupTableContextMenuConfig)
# 各基础对照表的「快速筛选」按钮(数据驱动,便于增减,无需改代码);
# key 与 context_menu.items[].table 一致,value 为该表按钮列表
quick_filters: dict[str, list[LookupTableQuickFilterConfig]] = Field(default_factory=dict)
class QuarantineActionConfig(_StrictModel):
"""隔离视图功能键/右键菜单项。"""
key: str
label: str
method: str
danger: bool = False
class QuarantineViewConfig(_StrictModel):
"""隔离视图 UI 配置(quarantine.view)。"""
label: str = ""
actions: list[QuarantineActionConfig] = Field(default_factory=list)
context_menu: list[QuarantineActionConfig] = Field(default_factory=list)
class QuarantineReleaseConfig(_StrictModel):
"""隔离释放行为配置(quarantine.release)。"""
require_order_identity: bool = True
rebuild_as: str = "sale_plan"
class QuarantineConfig(_StrictModel):
"""隔离/释放配置(quarantine.*,对应 quarantine.yaml 中的 quarantine 段)。"""
view: QuarantineViewConfig = Field(default_factory=QuarantineViewConfig)
release: QuarantineReleaseConfig = Field(default_factory=QuarantineReleaseConfig)
class SortRuleConfig(_StrictModel):
"""单条排序规则(sort_rules.keys[])。"""
field: str
order: str = "asc"
class SortRulesConfig(_StrictModel):
"""旧版排序规则配置(sort_rules.*,对应 grouping.yaml 中的 sort_rules 段)。"""
keys: list[SortRuleConfig] = Field(default_factory=list)
[文档]
class RootConfig(_StrictModel):
"""根配置模型
所有配置的顶层容器,聚合所有子配置模块.
顶层已闭环为 extra='forbid'(任务 2.3 / §六 UI 域严格校验),未建模键触发
ValidationError,杜绝顶层键拼写错误/死配置静默失效;嵌套 UI 段结构由
UIConfigModel 顶层闸门二次把关(见 ui_config.py)。
Attributes:
app: 应用基础配置
paths: 路径配置
sales_plan: 销售计划配置
certificate: 合格证配置
import_deduplication: 导入去重配置
database: 数据库配置
logging: 日志配置
ui: UI配置
color_mapping: 颜色映射配置
numbering: 编号规则配置(V2)
duplicate_check: 重复检查配置(V2)
unique_key: 唯一键配置(V2)
priority_rules: 优先级规则配置(V2)
export: 导出配置(V2)
sales_plan_fill: 销售计划填充配置(V2)
field_fallbacks: 字段回退链配置(V2)
field_fill_rules: 字段填充规则配置(V2)
sales_importer: 销售计划导入器配置(V2)
import_mapping: 导入列映射配置(V2)
export_debug: 导出调试配置(V2)
test_report: 试压报告配置(V2)
printer: 打印机配置(V2)
environment: 环境信息配置(V2)
colors: 颜色常量配置(V2)
vba_import: VBA导入配置(V2)
id_generator: 唯一键生成策略配置
production_status: 生产状态配置
import_config: 导入配置(与 QueryView 风格一致)
sync: 数据库同步配置(WebDAV)
print: 打印引擎配置
background: 底板预览配置
print_log_empty_markers: 打印日志空值默认标记配置
"""
# 任务 2.3(§15.3 阶段3 收官 / §六 UI 域严格校验闭环):顶层改为 extra=forbid。
# ui.yaml 经 !include 并入的 UI 顶层键(main_window/window/title_bar/nav_buttons/
# correction_queue/welcome_page/import_page/log_page/status_bar/styles/query_page)
# 已全部建模为宽松 dict/list 字段(见下方「UI 段顶层键」分组),由 forbid 闸门杜绝顶层键
# 拼写错误/死配置静默失效;嵌套结构仍松散(与 UIConfigModel 顶层 forbid 闸门分工一致)。
model_config = {"extra": "forbid"}
app: AppConfig = Field(default_factory=AppConfig)
paths: PathsConfig = Field(default_factory=PathsConfig)
sales_plan: SalesPlanConfig = Field(default_factory=SalesPlanConfig)
certificate: CertificateConfig = Field(default_factory=CertificateConfig)
import_deduplication: ImportDeduplicationConfig = Field(
default_factory=ImportDeduplicationConfig
)
database: DatabaseConfig = Field(default_factory=DatabaseConfig)
logging: LoggingConfig = Field(default_factory=LoggingConfig)
ui: UIConfig = Field(default_factory=UIConfig)
color_mapping: ColorMappingConfig = Field(default_factory=ColorMappingConfig)
# V2 新增顶层配置段(全部 Optional,保持向后兼容)
numbering: NumberingConfig | None = None
duplicate_check: DuplicateCheckConfig | None = None
unique_key: UniqueKeyConfig | None = None
priority_rules: PriorityRulesConfig | None = None
export: ExportConfig | None = None
sales_plan_fill: SalesPlanFillConfig | None = None
field_fallbacks: dict[str, list[str]] | None = None
field_fill_rules: dict[str, list[FieldFillRule]] | None = None
sales_importer: SalesImporterConfig | None = None
qualified_list: dict[str, Any] | None = (
None # 向后兼容,settings.py 中仍通过 cfg("qualified_list.*") 访问
)
import_mapping: ImportMappingConfig | None = None
export_debug: ExportDebugConfig | None = None
test_report: TestReportConfig | None = None
report: dict[str, Any] = Field(
default_factory=dict
) # report.yaml 顶层键(任务 2.3:补建模以闭环 forbid)
printer: PrinterConfig | None = None
environment: EnvironmentConfig | None = None
colors: ColorsConfig | None = None
field_templates: dict[str, Any] | None = (
None # 字段回填模板(Excel 字段映射格式,结构复杂保留 dict)
)
print_templates: dict[str, Any] | None = (
None # 打印模板坐标(来自 templates/coordinates.yaml,结构复杂保留 dict)
)
vba_import: VBAImportConfig | None = None
completed_orders_backfill: CompletedOrdersBackfillConfig | None = None
query_page: dict[str, Any] = Field(default_factory=dict)
# ── UI 段顶层键(任务 2.3:原为 !include 并入 RootConfig 顶层、靠 extra=allow 透传的
# 未建模键;现建模为宽松字段并配合顶层 forbid 闭环,杜绝顶层键拼写错误/死配置静默失效)──
main_window: dict[str, Any] = Field(default_factory=dict)
window: dict[str, Any] = Field(default_factory=dict)
title_bar: dict[str, Any] = Field(default_factory=dict)
nav_buttons: list[Any] = Field(default_factory=list)
correction_queue: dict[str, Any] = Field(default_factory=dict)
welcome_page: dict[str, Any] = Field(default_factory=dict)
import_page: dict[str, Any] = Field(default_factory=dict)
log_page: dict[str, Any] = Field(default_factory=dict)
status_bar: dict[str, Any] = Field(default_factory=dict)
styles: dict[str, Any] = Field(default_factory=dict)
# V3 新增分组排序配置
grouping: GroupingConfig | None = None
sorting: SortingConfig | None = None
# ── 新增模型化字段 ──
id_generator: IdGeneratorConfig | None = None # 唯一键生成策略
production_status: ProductionStatusConfig | None = None # 生产状态配置
import_config: ImportConfig | None = None # 导入配置
import_placeholder: ImportPlaceholderConfig | None = Field(
default_factory=ImportPlaceholderConfig, alias="import"
) # 导入期占位目录开关(B0-4)
bom_import: BomImportConfig | None = Field(
default_factory=BomImportConfig
) # BOM 导入控件配置(默认目录/默认模式)
sync: SyncConfig | None = None # 数据库同步配置(WebDAV)
print: PrintConfig | None = None # 打印引擎配置
background: BackgroundConfig | None = None # 底板预览配置
print_log_empty_markers: PrintLogEmptyMarkersConfig | None = None # 打印日志空标记
output: OutputConfig | None = None # 统一输出配置(OutputView 配置驱动,§11.5)
# §15 严格校验收尾(A-C):为 4 个业务段未建模顶层键补模型,使其受 forbid 保护
dictionary: DictionaryConfig = Field(default_factory=DictionaryConfig)
lookup_tables: LookupTablesConfig = Field(default_factory=LookupTablesConfig)
quarantine: QuarantineConfig = Field(default_factory=QuarantineConfig)
sort_rules: SortRulesConfig = Field(default_factory=SortRulesConfig)
[文档]
@field_validator("logging")
@classmethod
def validate_log_level(cls, v: LoggingConfig) -> LoggingConfig:
"""验证日志级别是否有效
Args:
v: 待验证的日志配置对象
Returns:
LoggingConfig: 验证通过的日志配置对象
Raises:
ValueError: 当日志级别不在有效值范围内时抛出
"""
valid_levels = {"DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"}
if v.level.upper() not in valid_levels:
raise ValueError(f"Invalid log level: {v.level}")
return v
# 导出所有模型
__all__ = [
"RootConfig",
"AppConfig",
"PathsConfig",
"TemplatePaths",
"SalesPlanConfig",
"ExcelReadConfig",
"HeaderDetectionConfig",
"DualFieldMappingConfig",
"SuffixRule",
"CertificateConfig",
"ImportDeduplicationConfig",
"DatabaseConfig",
"LoggingConfig",
"UIConfig",
"ColorMappingConfig",
# V2 新增
"NumberingConfig",
"NumberingSuffixRule",
"DuplicateCheckConfig",
"UniqueKeyConfig",
"UniqueKeyField",
"PriorityRulesConfig",
"ExportConfig",
"SalesImporterConfig",
"VBAImportConfig",
"PrintLayoutConfig",
"PrintFontConfig",
"PrintFieldConfig",
"TestReportConfig",
"PrinterConfig",
"EnvironmentConfig",
"ColorsConfig",
"ImportMappingConfig",
# V3 新增
"GroupingConfig",
"GroupingNumberingConfig",
"SortingConfig",
"SortingKey",
"CompletedOrdersBackfillConfig",
# 配置驱动新增
"ProductionStatusConfig",
"ProductionStatusUILabel",
"ProductionStatusAutoUpgradeRule",
"ProductionStatusInferenceRule",
"ProductionStatusUIConfig",
"ProductionStatusContextMenu",
"IdGeneratorConfig",
"ImportConfig",
"ImportViewConfig",
"ImportColumnConfig",
"ImportPlaceholderConfig",
"PrintConfig",
"PrintEscpConfig",
"SyncConfig",
"SyncWebdavConfig",
"BackgroundConfig",
"SalesPlanFillConfig",
"SalesPlanFillColumn",
"ExportDebugConfig",
"ExportDebugField",
"FieldFillRule",
"FieldFillTransform",
"PrintLogEmptyMarkersConfig",
# 统一输出配置驱动(§11.5)
"OutputConfig",
"OutputTargetConfig",
"HistoryBackfillConfig",
"OutputSharedConfig",
]