Files
cobol-java-v3/docs/detailed-design/07-config-system.md
T

475 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 07 - 配置系统详细设计
## 1. 模块概述
配置系统(`config/`)是 COBOL 迁移验证平台 V3 的全局管理模块,提供两级配置体系:
1. **全局配置**`Config`):项目级参数,包括 LLM 模型、超时、容忍度、Spark 配置、质量门限等
2. **程序级配置**`ProgramSchema`):每个 COBOL 程序的 DB 表定义、子程序列表、运行场景
此外,`MappingConfig` 提供 COBOL 字段到 Java 字段的映射配置,支持多种转换策略。
配置来源包括 TOML 文件(全局)、YAML 文件(程序级 schema 和字段映射)。
## 2. 文件清单
| 文件 | 职责 | 依赖 |
|------|------|------|
| `__init__.py` | 包入口,公开 API 导出 | `mapping`, `dataclasses` |
| `mapping.py` | COBOL-Java 字段映射配置 | `yaml`, `dataclasses` |
| `program_schema.py` | 程序级 DB schema + 运行场景定义 | `yaml`, `dataclasses` |
| `programs/*.yaml` | 58 个电信程序的 YAML schema 文件 | 无 |
## 3. 两级配置体系
### 3.1 架构图
```
全局配置 (Config)
|-- from_toml("aurak.toml")
|-- LLM 模型/超时/缓存
|-- Spark 配置
|-- 质量门限
|-- gixsql 配置
|
+-- 程序级配置 (ProgramSchema)
|-- load_schema("KYU04CAL")
|-- db_tables: [TableDef, ...]
|-- subprograms: [str, ...]
|-- runs: [ScenarioDef, ...]
|
+-- 字段映射 (MappingConfig)
|-- from_yaml("KYU04CAL.yaml")
|-- field_mappings: [FieldMapping, ...]
|-- redefines_strategy: dict
```
### 3.2 配置优先级
1. 命令行参数(最高)
2. 环境变量
3. TOML 配置文件
4. YAML schema 文件
5. 代码默认值(最低)
## 4. 全局配置(Config
### 4.1 数据类定义
```python
@dataclass
class Config:
project_name: str = ""
copybook_paths: list = ["./copybooks"]
dialect: str = "ibm"
llm_model: str = "deepseek-v4-flash"
llm_timeout: int = 120
llm_cache_dir: str = ".cache/llm"
coverage_default: str = "boundary"
rounding_mode: str = "TRUNCATE"
tolerance: float = 0.01
runner_mode: str = "native"
spark_master: str = "local[*]"
spark_input_format: str = "json"
num_records: int = 1000
branch_pass: float = 0.80
max_llm_cost: float = 0.50
quality_gate_mode: str = "warn"
quality_gate_decision_threshold: float = 0.90
quality_gate_paragraph_threshold: float = 1.0
gcov_enabled: bool = False
gcov_work_dir: str = ".gcov_output"
gcov_threshold: float = 0.5
max_quality_retries: int = 4
gixsql_path: str = "gixsql/bin/gixpp.exe"
gixsql_lib_path: str = "gixsql/lib"
gixsql_db_path: str = ".db/gixsql"
gixsql_compile_flags: str = "-fixed -ext cpy --coverage"
```
### 4.2 配置分组
| 分组 | 配置项 | 说明 |
|------|--------|------|
| 项目基础 | `project_name`, `copybook_paths`, `dialect` | 项目标识、COPYBOOK 路径、COBOL 方言 |
| LLM | `llm_model`, `llm_timeout`, `llm_cache_dir`, `max_llm_cost` | 模型选择、超时、缓存、成本控制 |
| 覆盖率 | `coverage_default`, `branch_pass`, `gcov_*` | 覆盖率目标、分支通过率、gcov 集成 |
| 比对 | `rounding_mode`, `tolerance` | 舍入模式、数值容忍度 |
| 运行器 | `runner_mode`, `spark_*`, `num_records` | native/spark 模式、Spark 参数 |
| 质量门限 | `quality_gate_*`, `max_quality_retries` | 决策阈值、段落阈值、重试次数 |
| gixsql | `gixsql_*` | DB 程序编译路径、库路径、编译标志 |
### 4.3 TOML 加载
```python
@classmethod
def from_toml(cls, path="aurak.toml") -> Config
```
TOML 文件结构:
```toml
[project]
name = "..."
copybook_paths = ["./copybooks"]
dialect = "ibm"
[llm]
model = "deepseek-v4-flash"
[coverage]
default_target = "boundary"
[comparison]
rounding_mode = "TRUNCATE"
default_tolerance = 0.01
[runner]
mode = "native"
[spark]
master = "local[*]"
num_records = 1000
[gixsql]
path = "gixsql/bin/gixpp.exe"
lib_path = "gixsql/lib"
db_path = ".db/gixsql"
compile_flags = "-fixed -ext cpy --coverage"
```
### 4.4 错误处理
- TOML 文件不存在或解析失败 -> 返回默认 `Config()` 实例
- 缺失字段使用默认值填充
## 5. 字段映射配置(mapping.py
### 5.1 数据类定义
```python
@dataclass
class FieldMapping:
cobol_field: str # COBOL 字段名
java_field: str # Java 字段名
field_type: str = "string" # 字段类型
precision: int = 0 # 精度(小数位)
trim: bool = False # 是否去空格
format: str = "" # 格式化规则
init_strategy: str = "auto" # 初始化策略
@dataclass
class MappingConfig:
program: str = "" # 程序 ID
dialect: str = "ibm" # COBOL 方言
field_mappings: list[FieldMapping] # 字段映射列表
redefines_strategy: dict # REDEFINES 处理策略
```
### 5.2 YAML 加载
```python
@classmethod
def from_yaml(cls, path: str) -> MappingConfig
```
YAML 文件结构:
```yaml
program: KYU04CAL
dialect: ibm
field_mapping:
- cobol_field: "BR-AMT"
java_field: "billAmount"
field_type: "decimal"
precision: 2
- cobol_field: "CUST-ID"
java_field: "customerId"
field_type: "string"
trim: true
redefines_strategy:
WS-BLOCK: "overlay"
```
### 5.3 字段查询
```python
def get_java_field(self, cobol_name: str) -> str
```
按 COBOL 字段名查找对应的 Java 字段名,未找到时返回原始 COBOL 字段名。
### 5.4 模块级断言
```python
_m = FieldMapping(cobol_field="BR-AMT", java_field="billAmount",
field_type="decimal", precision=2)
assert _m.cobol_field == "BR-AMT"
```
导入时自动执行结构验证。
## 6. 程序级 Schema 配置(program_schema.py
### 6.1 数据类定义
```python
@dataclass
class ColumnDef:
name: str # 列名
type: str # SQL 类型(CHAR(6), NUMERIC(4), VARCHAR(30)
primary_key: bool = False # 是否主键
nullable: bool = False # 是否可空
default: Optional[str] = None # 默认值
cobol_field: Optional[str] = None # 对应 COBOL 字段名
@dataclass
class TableDef:
name: str # 表名
columns: list[ColumnDef] # 列定义列表
create_if_missing: bool = True # 缺失时自动创建
sql_name: Optional[str] = None # COBOL SQL 中的表名(可能与 YAML 名不同)
@dataclass
class SysinDef:
period: str | None = "202607" # 处理期间
include_invalid_period: bool = False # 是否包含无效期间
modes: list[str] = ["NORMAL"] # 运行模式列表
final_mode: str = "RESET" # 最终模式
@dataclass
class ScenarioDef:
id: str # 场景 ID
sysin: SysinDef # SYSIN 卡片配置
inject_duplicate_pk: bool = False # 是否注入重复主键
row_overrides: dict[str, dict[str, str]] # 行级覆盖
delete_all_rows: bool = False # 是否清空所有行
drop_tables: list[str] = [] # 需要删除的表列表
command_line: str | None = None # 自定义命令行
seed_extra_rows: dict[str, int] = {} # 额外种子行数
@dataclass
class ProgramSchema:
program_id: str # 程序 ID
db_tables: list[TableDef] # DB 表定义列表
subprograms: list[str] # 子程序列表
db_type: str = "SQLite" # 数据库类型
db_name: str = "OVERTIME.DB" # 数据库文件名
runs: list[ScenarioDef] # 运行场景列表
coverage_dates: dict[str, list[dict]] # 覆盖率日期配置
command_line: str | None = None # 全局命令行
```
### 6.2 YAML 加载
```python
@classmethod
def from_yaml(cls, path: str | Path) -> ProgramSchema
```
### 6.3 Schema 查找
```python
def load_schema(program_id: str, search_dirs: list[str | Path] | None = None) -> ProgramSchema
```
`config/programs/` 目录下按 `{program_id}.yaml` 查找,未找到时抛出 `FileNotFoundError`
## 7. YAML Schema 配置
### 7.1 文件结构
每个程序对应一个 YAML 文件,位于 `config/programs/` 目录:
```
config/programs/
KYU04CAL.yaml
KYU05DED.yaml
KYU06UPD.yaml
KIN02UPD.yaml
KIN03EXP.yaml
KIN06CLD.yaml
KIN08DBU.yaml
KIN09CSV.yaml
SHA02MNC.yaml
SHA03MNP.yaml
SHA04TWO.yaml
SHA06TWM.yaml
SHA07KBR.yaml
ZAN06UPD.yaml
...
```
### 7.2 YAML Schema 模板
```yaml
program_id: {PROGRAM_ID}
db_type: SQLite
db_name: {DB_NAME}
db_tables:
- name: {COBOL_TABLE_NAME}
sql_name: {SQL_TABLE_NAME} # 可选,COBOL SQL 中的表名
columns:
- name: {COL_NAME}
type: {SQL_TYPE} # CHAR(N), NUMERIC(N,M), VARCHAR(N), DECIMAL(N,M), TIMESTAMP
primary_key: true/false
nullable: true/false
cobol_field: {COBOL_FIELD} # 可选,对应 COBOL 字段名
subprograms:
- {SUB_PROGRAM_ID}
runs:
- id: {SCENARIO_ID}
sysin:
period: "{YYYYMM}"
modes: ["NORMAL"]
inject_duplicate_pk: true/false
row_overrides:
{TABLE_NAME}:
{COL_NAME}: "{VALUE}"
delete_all_rows: true/false
drop_tables:
- {TABLE_NAME}
command_line: "{COMMAND}" # 可选
seed_extra_rows:
{TABLE_NAME}: {COUNT}
```
### 7.3 SQL 类型映射
| YAML 类型 | SQL 类型 | 说明 |
|-----------|----------|------|
| `CHAR(N)` | 定长字符 | COBOL DISPLAY 类型 |
| `VARCHAR(N)` | 变长字符 | COBOL X(n) 类型 |
| `NUMERIC(N)` | 整数 | 无小数位 |
| `NUMERIC(N,M)` | 定点数 | N 位总长,M 位小数 |
| `DECIMAL(N,M)` | 定点数 | 同 NUMERIC |
| `TIMESTAMP` | 时间戳 | 日期时间 |
### 7.4 运行场景(ScenarioDef
每个程序可定义多个运行场景,用于覆盖不同测试路径:
| 场景类型 | 典型配置 | 说明 |
|----------|----------|------|
| `normal` | 默认 SYSIN | 正常路径测试 |
| `collision` | `inject_duplicate_pk: true` | 主键冲突测试 |
| `abnormal` | 自定义 row_overrides | 异常路径测试 |
| 自定义 | `command_line` | 特殊命令行参数 |
### 7.5 SYSIN 卡片配置
SYSIN 卡片是 COBOL 程序的运行时输入,通过 `SysinDef` 配置:
- `period`:处理期间(如 `"202607"`),用于时间相关逻辑
- `modes`:运行模式列表(如 `["NORMAL"]`),影响程序分支
- `include_invalid_period`:是否包含无效期间测试
- `final_mode`:最终模式(如 `"RESET"`
## 8. 配置加载机制
### 8.1 加载流程
```
1. 读取 aurak.toml -> Config.from_toml()
2. 根据 program_id 查找 programs/{id}.yaml -> load_schema()
3. 可选: 读取字段映射 YAML -> MappingConfig.from_yaml()
4. 合并全局配置 + 程序级配置 -> 运行时参数
```
### 8.2 默认值策略
- 所有配置项均有代码级默认值
- TOML/YAML 仅覆盖显式指定的字段
- 缺失字段回退到 dataclass 默认值
### 8.3 已有程序 Schema 统计
截至当前,`config/programs/` 目录包含 14 个程序的 YAML schema
| 程序 ID | DB 表数 | 子程序数 | 运行场景数 |
|---------|---------|----------|------------|
| KYU04CAL | - | - | - |
| KYU05DED | - | - | - |
| KYU06UPD | - | - | - |
| KIN02UPD | - | - | - |
| KIN03EXP | - | - | - |
| KIN06CLD | - | - | - |
| KIN08DBU | - | - | - |
| KIN09CSV | - | - | - |
| SHA02MNC | - | - | - |
| SHA03MNP | - | - | - |
| SHA04TWO | - | - | - |
| SHA06TWM | - | - | - |
| SHA07KBR | 2 | 2 | 0 |
| ZAN06UPD | 2 | 5 | 3 |
## 9. 接口定义
### 9.1 模块公开 API
```python
# config/__init__.py
Config # 全局配置 dataclass
MappingConfig # 字段映射配置
FieldMapping # 单个字段映射
# config/mapping.py
MappingConfig.from_yaml(path) -> MappingConfig
MappingConfig.get_java_field(cobol_name) -> str
# config/program_schema.py
ProgramSchema.from_yaml(path) -> ProgramSchema
load_schema(program_id, search_dirs) -> ProgramSchema
```
### 9.2 典型使用模式
```python
from config import Config
from config.program_schema import load_schema
from config.mapping import MappingConfig
# 加载全局配置
config = Config.from_toml("aurak.toml")
# 加载程序 schema
schema = load_schema("KYU04CAL")
# 加载字段映射(如果存在)
mapping = MappingConfig.from_yaml("mappings/KYU04CAL.yaml")
java_field = mapping.get_java_field("BR-AMT")
# 使用配置
for table in schema.db_tables:
print(f"Table: {table.name}, SQL: {table.sql_name}")
for col in table.columns:
print(f" {col.name}: {col.type} (PK={col.primary_key})")
for scenario in schema.runs:
print(f"Scenario: {scenario.id}, Period: {scenario.sysin.period}")
```
## 10. 错误处理
### 10.1 错误策略
| 异常场景 | 处理方式 | 影响 |
|----------|----------|------|
| TOML 文件不存在 | 返回默认 Config | 使用代码默认值 |
| TOML 解析失败 | 返回默认 Config | 使用代码默认值 |
| YAML schema 不存在 | 抛出 FileNotFoundError | 流水线中断 |
| YAML 字段缺失 | 使用默认值填充 | 可能不完整 |
| SQL 类型无法识别 | 原样传递 | 下游处理 |
### 10.2 已知局限
1. `Config.from_toml` 吞掉所有异常,配置错误静默降级
2. `load_schema` 搜索路径硬编码为 `config/programs/`
3. `MappingConfig``redefines_strategy` 仅存储字典,无验证逻辑
4. `ProgramSchema``coverage_dates` 类型定义为 `dict[str, list[dict[str, str]]]`,缺乏 schema 验证
5. `SysinDef``period` 字段无格式校验,允许非法期间值
6. 程序级 YAML 无统一 schema 验证,依赖调用方保证格式正确