Files
cobol-java-v3/docs/detailed-design/08-data-flow.md
T

826 lines
30 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.
# 数据流设计文档
> 版本: v1.0 | 日期: 2026-08-22
> 本文档描述 COBOL 迁移验证平台 V3 的完整数据流,覆盖非 DB(flat file)管道和 DBSQL/SQLite)管道。
---
## 1. 数据流概述
### 1.1 设计目标
| 目标 | 说明 | 实现方式 |
|------|------|----------|
| **可追溯性** | 每条测试数据可追溯到源码决策点 | 路径约束 (field, op, value, want_true) 记录决策分支选择 |
| **一致性** | 非 DB 与 DB 管道共享同一套解析/生成引擎 | cobol_testgen/ 统一提供 extract_structure + generate_data |
| **容错性** | 单步失败不中断整体管道 | 每步返回 DbPipelineResult(success, data),失败立即终止后续 |
| **可观测性** | 全流程可监控、可调试 | 覆盖率报告、gcov 数据、JSON 中间产物、运行日志 |
### 1.2 管道对比
| 维度 | 非 DB 管道 | DB 管道 |
|------|-----------|---------|
| 编排器 | orchestrator.py | orchestrator_db.py |
| 运行器 | runners/cobol_runner.py | runners/gixsql_runner.py |
| 输入格式 | 固定长度平面文件 | 平面文件 + SQLite DB 种子 |
| 输出格式 | 平面文件 | 平面文件 + SQLite DB |
| 验证方式 | comparator/ 逐字段比对 | Step 6 验证 COBOL/Java 输出一致性 |
| 步骤数 | 4 步(生成-运行-比对-报告) | 6 步(环境-生成-运行-提取-Java-验证) |
---
## 2. 非 DB 管道数据流
### 2.1 总体流程
COBOL 源码 (.cbl)
|
v [输入阶段]
read.py: preprocess() + parse_data_division()
|
v [生成阶段]
core.py: build_branch_tree() -> branch_tree + assignments
|
v
design.py: enum_paths() -> path_infos
|
v
design.py: make_base_record() -> base_record
|
v
design.py: apply_constraint() -> records (测试数据)
|
v [运行阶段]
output.py: output_json() -> 测试数据 JSON
output.py: output_input_files() -> 固定长度平面文件
|
v
runners/cobol_runner.py: compile() + run() -> 输出文件
|
v [比对阶段]
comparator/aligner.py: 按主键对齐 COBOL/Java 记录
comparator/field_compare.py: 逐字段比较
comparator/normalizer.py: EBCDIC/COMP-3 标准化
|
v
report/generator.py: 生成 HTML 报告
### 2.2 输入阶段
**COBOL 源码 -> read.py 预处理 -> DATA DIVISION 解析 -> fields 结构信息**
COBOL 源码 (fixed/free format)
|
v read.preprocess()
+-- resolve_copybooks(): 展开 COPY 语句
+-- _is_fixed_format(): 自动检测 fixed/free 格式
+-- 展开至每行 <=72 字符 (fixed) 或保持原样 (free)
+-- 去除 EXEC CICS/SQL 块、逗号、ALL 关键字
|
v 预处理后源码 (preprocessed)
|
v read.extract_data_division()
+-- 提取 DATA DIVISION 文本块
|
v read.parse_data_division()
+-- Lark grammar.lark 解析 (Earley parser, dynamic lexer)
+-- 逐行解析 FieldDef: level, name, PIC, USAGE, VALUE, REDEFINES, OCCURS
+-- PIC 子句解析 -> PicInfo (type, digits, decimal, length, signed)
+-- 返回 list[FieldDef]
|
v expand_occurs()
+-- OCCURS 展开为下标副本: WS-CELL(1), WS-CELL(2), ...
**输出数据结构:**
# fields: list[dict] -- 每个字段一个 dict
{
'name': 'R01EMP-ID', # 字段名 (大写)
'level': 1, # 层号 (01, 05, 77, 88)
'pic': 'X(8)', # PIC 子句原始文本
'pic_info': { # PIC 解析结果
'type': 'alphanumeric', # numeric | alphanumeric | alphabetic
'digits': 0, # 整数位数 (numeric)
'decimal': 0, # 小数位数 (numeric)
'length': 8, # 总长度 (alphanumeric)
'signed': False,
},
'section': 'FILE', # DATA DIVISION 节
'occurs': 0, # OCCURS 次数
'redefines': None, # REDEFINES 目标
'usage': None, # COMP | COMP-3 | BINARY | DISPLAY
'is_88': False, # 是否 88 级条件
'parent': None, # 88 级父字段名
'value': None, # VALUE 子句
'values': None, # 88 级多值列表
'is_filler': False,
}
### 2.3 生成阶段
**fields -> core.py build_branch_tree -> design.py enum_paths -> make_base_record -> apply_constraint -> 测试数据**
#### 2.3.1 分支树构建
PROCEDURE DIVISION 文本
|
v core.build_branch_tree_fallback()
+-- pipeline_bridge: 3秒超时桥接
| +-- 优先: procedure_parser.py (新解析器, 行级状态机)
| +-- 回退: core._BrParser (旧解析器, 正则驱动)
|
+-- 段落扫描: scan_paragraphs() -> {name: (start, end)}
+-- 逐段解析 IF / EVALUATE / PERFORM / READ / WRITE / MOVE / COMPUTE
+-- 返回 (branch_tree: BrSeq, assignments: dict)
**分支树节点类型:**
| 节点 | 类 | 属性 | 说明 |
|------|----|------|------|
| 序列 | BrSeq | children: list | 顺序执行的语句序列 |
| 条件 | BrIf | condition, cond_tree, true_seq, false_seq | IF-ELSE 分支 |
| 评估 | BrEval | subject, subjects, when_list, other_seq | EVALUATE 多分支 |
| 循环 | BrPerform | perf_type, condition, body_seq | PERFORM UNTIL/VARYING |
| 查找 | BrSearch | table_name, is_all, when_list | SEARCH/SEARCH ALL |
| 赋值 | Assign | target, source_info | MOVE/COMPUTE/ADD/SUBTRACT/MULTIPLY/DIVIDE |
| 调用 | CallNode | program_name, using_params | CALL 子程序 |
| 跳转 | GoTo | target, body_seq | GO TO |
| 退出 | ExitNode | exit_type | EXIT PARAGRAPH/PERFORM/PROGRAM |
#### 2.3.2 路径枚举
branch_tree + assignments
|
v design_mcdc.enum_paths() / design.enum_paths()
+-- 遍历分支树,收集每个决策点 (BrIf/BrEval/BrPerform)
+-- 对每个决策点生成 True/False 或 WHEN 分支路径
+-- MC/DC 约束集: cond.mcdc_sets() -> 每个叶条件的 T/F 独立约束
+-- 路径约束合并: prior_false 累积(EVALUATE WHEN 入口条件)
+-- PERFORM VARYING: 最后一次迭代约束 (last-iteration)
+-- SEARCH: 表索引约束 + AT END 分支
+-- 返回 path_infos: list[(constraints, path_assignments, term_type)]
**路径约束格式:**
# path_infos 的每个元素
(
# constraints: Path = list[Constraint]
[
('WRK-MONTH', '>', '0', True), # (field, op, value, want_true)
('WRK-MONTH', '<', '13', True),
('R01EMP-ID', '<>', '00000000', True),
],
# path_assignments: dict -- 赋值表
{
'WRK-PREV-EMP-ID': [{'type': 'move', 'source_vars': ['R01EMP-ID']}],
'DBV-EMP-ID': [{'type': 'move_literal', 'literal': 'A0000001'}],
},
# term_type: str
'normal' # 或 'abend'
)
#### 2.3.3 测试数据记录生成
path_infos + data_fields
|
v design.generate_records()
|
+-- 遍历每条路径 (seq=1,2,3...):
| |
| +-- make_base_record(seq, data_fields)
| | +-- 按 VALUE 子句设置初始值
| | +-- 按 PIC 类型生成默认值:
| | | +-- numeric: _make_numeric_value(idx, seq, total_digits)
| | | +-- alphanumeric: _make_alpha_value(idx, seq, length)
| | | +-- date: seq_date(record_num) -> YYYYMMDD
| | +-- 返回 base_record: dict {field_name: value}
| |
| +-- Pass A: propagate_assignments(rec, path_assign, data_fields)
| | +-- 模拟赋值传播: MOVE/COMPUTE/READ INTO 等
| |
| +-- Pass B: apply_constraint(rec, field, op, val, want, ...)
| | +-- trace_to_root(): 沿 MOVE 链追溯到根字段
| | +-- invert_through_chain(): 反向求解约束值
| | +-- satisfying_value(): 满足约束的值计算
| | | +-- numeric: 边界值 +/-1
| | | +-- alphanumeric: 字典序边界
| | | +-- date: YYYYMMDD 格式边界
| | +-- 递归写入 base_record[field] = 满足值
| |
| +-- Pass B.5: forward propagate (变量间 MOVE 一致性)
| +-- Pass B.75: COMPUTE 重算 (约束修改源字段后)
| +-- get_term_type(path_cons) -> (filtered_cons, term_type)
|
+-- 返回 (records, kept_path_cons, term_types)
**测试数据记录格式:**
# records: list[dict] -- 每条记录一个 dict
[
{
'R01EMP-ID': 'A0000001',
'R01DATE': '20250101',
'R01LINE': '0001',
'WRK-PREV-EMP-ID': '',
'WRK-MONTH': '06',
'DBV-EMP-ID': 'A0000001',
'_assigned_fields': {'R01EMP-ID', 'R01DATE'}, # 内部标记
'_w02_path': True, # PREV 连锁标记
},
# ... 更多记录
]
### 2.4 运行阶段
**测试数据 -> output.py -> cobol_runner compile/run -> 输出文件**
records + fd_fields + open_dir
|
v output.output_json()
+-- 按 FD 分组: field_to_fd 映射字段到 FD
+-- 按 open_dir 确定方向: INPUT/OUTPUT/I-O
+-- 按 roles 分类: input/inout -> input 块, output/inout -> expected_output 块
+-- 不属于任何 FD 的字段 -> working_storage 块
+-- 输出 JSON: {program, records: [{input, expected_output, working_storage, termination}]}
|
v output.output_input_files()
+-- 仅处理 INPUT/I-O 方向的 FD
+-- 按 FD 分组,每 FD 输出一个 JSON: {stem}_{fd_name}.json
+-- abend 记录单独输出: {stem}_abend_{fd_name}.json
+-- 二进制模式: 按 field offsets 打包为固定长度二进制文件
|
v flatfile.write_all_files()
+-- analyze_fd_layout(): 解析 FD 记录布局 (字段名/PIC/offset/length)
+-- 按 FD 布局序列化每条记录为固定长度字节
+-- write_flat_file(): 输出到 outdir/{assign_name}
|
v runners/cobol_runner.compile()
+-- cobc -std=ibm -free -x src.cbl -o exe
|
v runners/cobol_runner.run()
+-- 设置 CWD,复制输入文件到 input/
+-- subprocess.run(exe) 执行
+-- 捕获 stdout/stderr,收集输出文件
+-- 返回 RunResult(success, records, log)
### 2.5 比对阶段
**输出文件 -> aligner.py -> field_compare.py -> normalizer.py -> report/generator.py -> HTML 报告**
COBOL 输出文件 + Java 输出文件
|
v comparator/aligner.py: align_records()
+-- 按主键字段 (如 CUST-ID) 分组
+-- 取两侧键的并集,按字符串排序
+-- 逐键配对: MATCHED / MISSING_IN_SPARK / EXTRA_IN_SPARK
+-- 返回 list[(cobol_record, java_record, status)]
|
v comparator/normalizer.py: normalize_encoding()
+-- EBCDIC -> ASCII 解码 (EBCDIC_037 映射表)
+-- COMP-3 压缩十进制解码 (nibble -> decimal)
+-- 日期格式标准化 (YYYYMMDD -> YYYY-MM-DD)
|
v comparator/field_compare.py: compare_field()
+-- 按字段类型选择比较策略:
| +-- numeric: Decimal 精度比较 + 容差 (tolerance=0.01)
| +-- date: YYYYMMDD 格式归一化后比较
| +-- string: strip 后直接比较
+-- 返回 FieldResult(field_name, status, cobol_value, java_value)
+-- status: PASS / MISMATCH / TOLERATED / NOT_SET
|
v comparator/rounding_detect.py: detect_rounding()
+-- 判断数值差异是否由 COBOL ROUNDED 子句引起
+-- 计算舍入误差范围
|
v report/generator.py: ReportGenerator
+-- generate_json(): 输出 JSON 报告 (VerificationRun 数据)
+-- generate_html(): 输出 HTML 报告
| +-- 覆盖率卡片: 段落覆盖率、分支覆盖率、决策点覆盖率
| +-- HINA 卡片: 判定类型、确信度
| +-- 质量评分卡片: quality_score
| +-- 重试历史卡片: heal_retry, simple_retry
| +-- 字段比对表格: PASS/MISMATCH 高亮
+-- 返回报告文件路径
---
## 3. DB 管道数据流
### 3.1 总体 6 步流程
COBOL 源码 (含 EXEC SQL)
|
v [Step 1: 环境整备]
step1_setup_environment()
+-- 复制源码到 ASCII 工作目录
+-- gixpp 预处理 + CONNECT TO 路径修补
+-- cobc 编译 -> exe_path
|
v [Step 2: 输入数据生成]
step2_generate_inputs(scenario)
+-- extract_structure() -> 分支树 + 赋值表
+-- generate_all_data() -> 测试数据记录
+-- _init_database() + _populate_database() -> SQLite DB 种子
+-- flatfile.write_all_files() -> 平面文件
|
v [Step 3: COBOL 执行]
step3_run_cobol(scenario)
+-- runner.run() -> 输出文件 + gcov 数据
|
v [Step 4: 中间数据提取]
step4_extract_intermediate()
+-- SELECT * FROM each table -> W01 JSON
|
v [Step 5: Java 执行] (可选)
step5_run_java()
+-- java -jar migration.jar -i W01.json -o output/
|
v [Step 6: 验证]
step6_verify()
+-- 比较 Java 输出与 COBOL 期望值
+-- 返回 VerificationRun (PASS/MISMATCH)
### 3.2 Step 1: 环境整备 (step1_setup_environment)
**职责:** gixpp 预处理 + cobc 编译
cobol_src_dir/{program_id}.cbl
|
v _copy_sources_to_workdir()
+-- 复制主源码 {program_id}.cbl -> work_dir/src/
+-- 复制 COPYBOOK (*.cpy) -> work_dir/src/
+-- 复制子程序 (SUB*.cbl) -> work_dir/src/
(搜索: cobol_src_dir, sub/, production/sub/, cobol-tna-system/sub/)
|
v runner.preprocess(src, preprocessed/, copybook_dirs)
+-- gixpp 预处理 + CONNECT TO 路径修补
| gixpp 错误转换: 'data/kin.db' -> 'sqlite://localhost/kin'
| 修补为: 'sqlite:///{db_path}'
|
v runner.compile(pp, exe, copybook_dirs, extra_srcs)
+-- cobc 编译 -> work_dir/bin/{program_id}.exe
+-- 编译日志写入 runtime_dir/logs/compile/
|
v 返回 DbPipelineResult(step=1, success, data={exe_path, log})
**输入:** cobol_src_dir, copybook_dirs, schema.subprograms
**输出:** src_path, pp_path, exe_path
### 3.3 Step 2: 输入数据生成 (step2_generate_inputs)
**职责:** COBOL 解析 -> 测试数据生成 -> DB 初始化 -> 平面文件输出
src_text + schema + scenario
|
v COBOL 解析
+-- extract_structure(src_text) -> 分支树 + 赋值表
+-- generate_all_data() -> 测试数据记录 (白盒+机能+策略)
+-- 后处理: R02APPL-ID 链接 R01APPL-ID
|
v DB 初始化
+-- 确定 DB 路径(场景分离: {program_id}_{scenario_id}.db
+-- 清理旧 DB -> _init_database(db_path)
| +-- _create_tables(): 按 YAML schema 创建表 + 主键
+-- _populate_database(): 注入种子行
| +-- 解析 COBOL -> 分支树 -> 路径枚举
| +-- build_db_input(): 生成 DB 输入行
| | +-- collect_sql_meta(): 提取 SQL 元数据 (SELECT/INSERT/UPDATE/DELETE)
| | +-- _hostvar_root(): WHERE 宿主变量 MOVE 链解析
| | +-- _resolve_where_hostvar(): 宿主变量追溯到输入记录根字段
| | +-- 用输入键建种子(与运行时一致)
| +-- 覆盖率驱动数据补充(日期、假期等)
| +-- 区间协调(INSURANCE-RATES / EMP-MASTER
| +-- INSERT OR IGNORE 写入 DB
+-- _inject_extra_seed_rows(): 大结果集注入
+-- _inject_sql_error_rows(): PK 冲突行注入
|
v 记录修补
+-- records[0].R01EMP-ID = SPACE(触发空社员路径)
+-- 全零 EMP-ID -> SPACE 清洗
+-- 注入重复 EMP-IDAGG UPDATE 路径)
+-- _deduplicate_r01_pk(): PK 去重
+-- _inject_aggregation_boundaries(): 聚合边界数据
|
v 平面文件输出
+-- write_all_files(): 全 FD 平面文件
+-- write_sysin_file(): SYSIN 配置
|
v 返回 DbPipelineResult(step=2, data={records, flat_files, db_path})
**输入:** src_path, pp_path, schema, scenario
**输出:** generated_records, generated_structure, db_path, 平面文件
### 3.4 Step 3: COBOL 执行 (step3_run_cobol)
**职责:** 调用编译后的 COBOL 程序并收集 gcov 覆盖率数据
exe_path + schema + scenario
|
v 环境准备
+-- 创建 runtime/run_{id}/main/{input,output}/ 目录
+-- 复制生成的平面文件 -> input/
+-- 复制 JSON -> json/
|
v 文件方向映射 (_scan_assign_to)
+-- 正则扫描 SELECT/ASSIGN-TO -> {文件名: 方向}
+-- OPEN 语句解析 -> INPUT/OUTPUT 方向确定
|
v DB 路径准备
+-- 场景 DB -> 复制到默认 DB 路径
+-- CWD/data/kin.dbCONNECT TO 路径)
|
v 执行
+-- 清理前次 .gcda 文件
+-- runner.run(exe, cwd, db_path, env_overrides, command_args)
+-- 日志写入 runtime_dir/logs/
|
v gcov 数据收集
+-- .gcda 从 CWD + exe_dir 复制到 gcov/run_{id}/
+-- .gcno 同步(共享 .gcnoCOPY 不 MOVE
|
v 返回 DbPipelineResult(step=3, data={returncode, log, ...})
### 3.5 Step 4: 中间数据提取 (step4_extract_intermediate)
**职责:** 从 SQLite DB 导出 Java 程序所需的 JSON 中介数据
_current_db_path + schema.db_tables
|
v
+-- 打开 DB
+-- 遍历 schema.db_tables,对每张表执行 SELECT * FROM [table]
+-- 构建 meta = {program_id, tables: {table_name: [rows]}}
+-- 写入 work_dir/intermediate/{program_id}_W01.json
+-- 返回 DbPipelineResult(step=4, data={tables, w01_path})
### 3.6 Step 5: Java 执行 (step5_run_java)
**职责:** 调用 Java 转换程序处理 COBOL 输出数据
java_input_path + java_jar
|
v
+-- 创建 java_output 目录
+-- 构建命令: java -jar {java_jar} -i {java_input_path} -o {java_out}
+-- subprocess.run(cmd, capture_output=True, timeout=60)
+-- 返回 DbPipelineResult(step=5, data={returncode, log})
### 3.7 Step 6: 结果验证 (step6_verify)
**职责:** 比较 Java 输出与 COBOL 期望值
java_output_path + _current_db_path
|
v
+-- 构建 VerificationRun 结果对象
+-- 读取 DB 各表行数(调试信息)
+-- 扫描 java_output_path 下的 .txt/.json 文件
+-- 设置 exit_code 和 statusPASS/MISMATCH
+-- 返回 VerificationRun
---
## 4. 核心数据结构
### 4.1 fields 列表格式
fields 是整个数据流的核心数据结构,贯穿输入-生成-运行全流程。
# 类型: list[dict]
# 来源: read.parse_data_division() + expand_occurs()
# 用途: 分支树构建、路径枚举、约束应用、JSON 输出
[
{
'name': str, # 字段名 (大写, 如 'R01EMP-ID')
'level': int, # 层号 (01, 05, 77, 88)
'pic': str | None, # PIC 子句原始文本
'pic_info': { # PIC 解析结果
'type': str, # 'numeric' | 'alphanumeric' | 'alphabetic' | 'unknown'
'digits': int, # 整数位数 (numeric)
'decimal': int, # 小数位数 (numeric)
'length': int, # 总长度 (alphanumeric)
'signed': bool, # 是否有符号
},
'section': str, # 'FILE' | 'WORKING-STORAGE' | 'LINKAGE'
'occurs': int, # OCCURS 次数 (0=无)
'occurs_depending': str | None, # OCCURS DEPENDING ON 目标
'redefines': str | None, # REDEFINES 目标字段名
'usage': str | None, # 'COMP' | 'COMP-3' | 'BINARY' | 'PACKED-DECIMAL' | 'DISPLAY'
'is_88': bool, # 是否 88 级条件
'parent': str | None, # 88 级父字段名
'value': str | None, # VALUE 子句值
'values': list[str] | None, # 88 级多值列表
'is_filler': bool, # 是否 FILLER
},
# ... 更多字段
]
### 4.2 Constraint 约束格式
Constraint 是路径枚举和约束应用的基本单元。
# 类型: tuple (4 元组)
# 定义: models.py 中 Constraint = tuple
# 格式: (field, operator, value, want_true)
Constraint = (
str, # field: 字段名 (如 'WRK-MONTH', 'R01EMP-ID')
str, # operator: 比较运算符 ('=' | '<>' | '>' | '<' | '>=' | '<=' | 'not_in')
str, # value: 比较值 (字符串形式, 如 '12', '00000000', 'SPACE')
bool, # want_true: True=满足条件, False=不满足条件
)
# 示例:
('WRK-MONTH', '>', '0', True) # WRK-MONTH > 0 为真
('R01EMP-ID', '<>', '00000000', True) # R01EMP-ID 不等于 00000000 为真
('SQLCODE', '=', '100', False) # SQLCODE = 100 为假 (即 SQLCODE != 100)
### 4.3 Path 路径格式
Path 是一条完整执行路径的所有约束集合。
# 类型: list[Constraint]
# 定义: models.py 中 Path = list[Constraint]
# 含义: 所有约束同时满足时,程序沿该路径执行
Path = [
('WRK-MONTH', '>', '0', True),
('WRK-MONTH', '<', '13', True),
('R01EMP-ID', '<>', '00000000', True),
('__DP', '=', 'T', True), # 决策点标记 (设计内部使用)
]
# path_infos 格式 (生成记录的输入):
path_infos = [
# (constraints, path_assignments, term_type)
(
[Constraint, ...], # 路径约束列表
{str: list[dict]}, # 赋值表 (目标 -> 赋值操作列表)
'normal' | 'abend', # 终止类型
),
# ... 更多路径
]
# 赋值表 (path_assignments) 格式:
{
'WRK-PREV-EMP-ID': [
{'type': 'move', 'source_vars': ['R01EMP-ID']}
],
'DBV-EMP-ID': [
{'type': 'move_literal', 'literal': 'A0000001'}
],
'WS-COUNT': [
{'type': 'compute', 'op': 'add', 'left': 'WS-COUNT', 'right': '1'}
],
}
### 4.4 测试数据 JSON 格式
测试数据 JSON 是非 DB 管道和 DB 管道共享的输出格式。
{
'program': str, # 程序名 (如 'KIN04CHK')
'records': [
{
'input': { # 按 FD 分组的输入字段
'FD_NAME': {
'FIELD1': str,
'FIELD2': str,
...
}
},
'expected_output': { # 按 FD 分组的期望输出字段
'FD_NAME': {
'FIELD1': str,
...
}
},
'working_storage': { # 不属于任何 FD 的工作存储字段
'WRK-MONTH': str,
'WS-COUNT': str,
...
},
'termination': str, # 'normal' | 'abend'
},
# ... 更多记录
],
'db_input': { # DB 管道专用: DB 种子数据 (可选)
'table_name': [
{'col1': val1, 'col2': val2, ...},
...
]
}
}
---
## 5. 数据流图
### 5.1 非 DB 管道完整流程 (Mermaid)
`mermaid
flowchart TD
A[COBOL 源码 .cbl] --> B[read.py preprocess]
B --> C[read.py parse_data_division]
C --> D[expand_occurs]
D --> E[fields: list dict]
E --> F[core.py build_branch_tree]
F --> G[branch_tree + assignments]
G --> H[design.py enum_paths]
H --> I[path_infos]
I --> J[design.py generate_records]
E --> J
J --> K[records: list dict]
K --> L[output.py output_json]
L --> M[测试数据 JSON]
K --> N[output.py output_input_files]
K --> O[flatfile.write_all_files]
N --> P[固定长度平面文件]
O --> P
P --> Q[cobol_runner compile]
Q --> R[cobol_runner run]
R --> S[COBOL 输出文件]
S --> T[comparator/aligner align_records]
U[Java 输出文件] --> T
T --> V[记录对 pairs]
V --> W[comparator/normalizer]
W --> X[comparator/field_compare]
X --> Y[FieldResult list]
Y --> Z[report/generator generate_html]
Z --> AA[HTML 验证报告]
`
### 5.2 DB 管道完整流程 (Mermaid)
`mermaid
flowchart TD
A[COBOL 源码 含 EXEC SQL] --> B[Step 1: 环境整备]
B --> B1[gixpp 预处理]
B1 --> B2[cobc 编译]
B2 --> B3[exe_path]
A --> C[Step 2: 输入数据生成]
B3 --> C
C --> C1[extract_structure]
C1 --> C2[generate_all_data]
C2 --> C3[records]
C3 --> C4[flatfile.write_all_files]
C4 --> C5[平面文件 input/]
C3 --> C6[build_db_input]
C6 --> C7[INSERT INTO SQLite DB]
C7 --> C8[db_path]
C5 --> D[Step 3: COBOL 执行]
C8 --> D
D --> D1[runner.run]
D1 --> D2[COBOL 输出文件]
D1 --> D3[gcov 数据]
D2 --> E[Step 4: 中间数据提取]
C8 --> E
E --> E1[SELECT * FROM tables]
E1 --> E2[W01 JSON]
E2 --> F[Step 5: Java 执行 可选]
F --> F1[java -jar migration.jar]
F1 --> F2[Java 输出文件]
F2 --> G[Step 6: 验证]
D2 --> G
G --> G1[VerificationRun]
G1 --> G2[PASS / MISMATCH]
`
### 5.3 数据流关键节点汇总
`mermaid
flowchart LR
subgraph 输入层
A1[COBOL 源码]
A2[COPYBOOK]
A3[YAML schema]
end
subgraph 解析层
B1[preprocess]
B2[parse_data_division]
B3[build_branch_tree]
end
subgraph 生成层
C1[enum_paths]
C2[generate_records]
C3[build_db_input]
end
subgraph 输出层
D1[output_json]
D2[write_all_files]
D3[write_sysin_file]
end
subgraph 执行层
E1[cobol_runner]
E2[gixsql_runner]
E3[java -jar]
end
subgraph 验证层
F1[aligner]
F2[field_compare]
F3[coverage]
F4[report]
end
A1 --> B1 --> B2 --> B3
A2 --> B1
A3 --> C3
B3 --> C1 --> C2
C2 --> D1
C2 --> D2
C3 --> D2
C3 --> D3
D2 --> E1
D2 --> E2
D3 --> E2
E1 --> F1
E2 --> F1
E3 --> F1
F1 --> F2 --> F4
B3 --> F3 --> F4
`
---
## 6. 跨模块数据流转
### 6.1 模块间数据传递关系
| 源模块 | 目标模块 | 传递数据 | 数据格式 |
|--------|----------|----------|----------|
| read.py | core.py | preprocessed, data_fields | str, list[dict] |
| read.py | design.py | data_fields, open_dir, file_sec | list[dict], dict, dict |
| core.py | design.py | branch_tree, assignments | BrSeq, dict |
| cond.py | design.py | path constraints | list[Constraint] |
| design.py | output.py | records, kept_path_cons, term_types | list[dict], list, list[str] |
| design.py | to_sql.py | records, data_fields, assignments | list[dict], list[dict], dict |
| output.py | runner.py | records (JSON/binary) | dict, bytes |
| runner.py | comparator/ | output files | file paths |
| to_sql.py | orchestrator_db.py | db_input (DB seed rows) | dict[str, list[dict]] |
| flatfile.py | orchestrator_db.py | flat files (binary) | file paths |
| orchestrator_db.py | gixsql_runner.py | exe_path, db_path, env | Path, Path, dict |
| gixsql_runner.py | orchestrator_db.py | RunResult | dataclass |
| comparator/aligner.py | comparator/field_compare.py | aligned pairs | list[tuple] |
| comparator/field_compare.py | report/generator.py | FieldResults | list[FieldResult] |
| coverage.py | report/generator.py | DecisionPoints, coverage rates | list[DecisionPoint], float |
| data_merger.py | __init__.py | merged records | list[dict] |
| hina/strategy.py | data_merger.py | strategy records | list[dict] |
### 6.2 核心数据流路径
**路径 1: 非 DB 管道 (flat file)**
read.py (fields) -> core.py (branch_tree) -> design.py (records)
-> output.py (JSON + flat files) -> cobol_runner (output files)
-> comparator/ (verification results) -> report/ (HTML)
**路径 2: DB 管道 (SQL/SQLite)**
read.py (fields) -> core.py (branch_tree) -> design.py (records)
-> to_sql.py (DB seed rows) -> flatfile.py (flat files)
-> gixsql_runner (output files + DB state)
-> step4 (W01 JSON) -> step5 (Java output) -> step6 (verification)
**路径 3: 覆盖率收集**
core.py (branch_tree) -> coverage.py (decision_points)
-> mark_coverage (covered branches) -> gcov.py (dynamic coverage)
-> coverage.py (merged coverage) -> report/ (HTML report)
### 6.3 关键中间产物
| 产物 | 生成位置 | 消费位置 | 格式 |
|------|----------|----------|------|
| preprocessed | read.py | core.py, read.py | str (预处理后源码) |
| data_fields | read.py | core.py, design.py, to_sql.py | list[dict] |
| branch_tree | core.py | design.py, coverage.py | BrSeq |
| assignments | core.py | design.py, to_sql.py | dict |
| path_infos | design.py | design.py (generate_records) | list[tuple] |
| records | design.py | output.py, to_sql.py, flatfile.py | list[dict] |
| fd_fields | read.py | output.py | dict[str, set[str]] |
| open_dir | read.py | output.py, flatfile.py | dict[str, str] |
| file_sec | read.py | output.py, __init__.py | dict[str, list[str]] |
| db_input | to_sql.py | orchestrator_db.py | dict[str, list[dict]] |
| flat files | flatfile.py | runner.py | file paths (binary) |
| JSON | output.py | runner.py, coverage.py | file path |
| gcov data | gcov.py | coverage.py | dict[int, int] |
| DecisionPoints | coverage.py | report/generator.py | list[DecisionPoint] |
| VerificationRun | comparator/ | report/generator.py | dataclass |
| FieldResult | field_compare.py | report/generator.py | dataclass |