# 数据流设计文档 > 版本: v1.0 | 日期: 2026-08-22 > 本文档描述 COBOL 迁移验证平台 V3 的完整数据流,覆盖非 DB(flat file)管道和 DB(SQL/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-ID(AGG 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.db(CONNECT 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 同步(共享 .gcno,COPY 不 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 和 status(PASS/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 |