docs: add V3 detailed design documents, AI usage log, and development paradigm

This commit is contained in:
hangshuo652
2026-08-22 18:05:24 +08:00
parent f67a5bf769
commit d1f3d69445
13 changed files with 5248 additions and 0 deletions
+40
View File
@@ -27,6 +27,45 @@
参见 `.code-review.yaml` 调整 review 严苛程度(fast / standard / strict)。
## AI 使用日志(自动执行)
每次创建或修改代码文件后,**必须**在项目根目录的 `_AI_USAGE_LOG.md` 中追加一条记录。
### 触发规则
- **自动触发**AI 生成或修改 `.py` / `.cbl` / `.cpy` / `.lark` / `.md` 等文件后
- **每次操作只记录一次**:同一批修改合并为一条记录
### 记录格式
```markdown
### YYYY-MM-DD HH:MM:SS - 阶段名称
- **范式步骤:** 阶段描述(如:功能开发、测试、修复、文档、配置管理)
- **修改摘要:** 简要说明做了什么
- **涉及文件:** 列出主要修改的文件路径
- **使用模型:** deepseek
```
### 阶段分类
| 阶段 | 说明 |
|------|------|
| 功能开发 | 新增功能、模块、API |
| 测试 | 单元测试、集成测试、E2E 测试 |
| 修复 | Bug 修复、代码审查问题修复 |
| 文档 | README、设计文档、注释 |
| 配置管理 | .gitignore、配置文件、工具集成 |
### 示例
```markdown
### 2026-08-22 16:30:00 - 功能开发
- **范式步骤:** 新增用户登录接口
- **修改摘要:** 添加 login.py 和相关测试
- **涉及文件:** `cobol_testgen/login.py`, `tests/test_login.py`
- **使用模型:** deepseek
```
## 项目结构
```
@@ -35,4 +74,5 @@ test-data/ # 测试套件
benchmark-programs/ # 测试基准(58 电信程序)
.claude/skills/ # 项目级 skills
.code-review.yaml # code-review 配置
_AI_USAGE_LOG.md # AI 使用日志(自动维护)
```
+554
View File
@@ -0,0 +1,554 @@
# AI 使用日志
> 本文件记录项目开发过程中 AI 辅助的所有代码修改,按时间倒序排列。
---
### 2026-08-22 20:30:00 - 文档阶段
- **范式步骤:** HINA分类系统详细设计文档编写
- **修改摘要:** 创建04-hina-classification.md,包含模块概述、文件清单、分类算法、确信度计算、质量门禁、gcov收集、接口定义、错误处理等14个章节
- **涉及文件:** `docs/detailed-design/04-hina-classification.md`
- **使用模型:** deepseek
## 2026-08-09
### 2026-08-09 17:43:28 - 文档阶段
- **范式步骤:** 详细设计文档编写
- **修改摘要:** 添加詳細設計書(COPY 反復拓撃、DB 定義拓撃、各程序詳細設計書等 21 个文件)
- **涉及文件:** 詳細設計書/COPY反復拓撃.md, 詳細設計書/DB定義拓撃.md, 詳細設計書/詳細設計書_KIN01INP.md 等
- **使用模型:** deepseek
### 2026-08-09 17:43:16 - 测试阶段
- **范式步骤:** 单元测试与回归测试
- **修改摘要:** 补充 between/hostvar/gcov-merge、class conditions、schema drop-tables、gixsql 相关测试用例(21 个测试文件)
- **涉及文件:** tests/cobol_testgen/test_to_sql_between.py, tests/cobol_testgen/test_to_sql_between_dbinput.py 等
- **使用模型:** deepseek
### 2026-08-09 17:43:00 - 功能开发阶段
- **范式步骤:** 核心功能增强
- **修改摘要:** SQL between/hostvar-key 对齐、class-condition 解析、多场景 gcov 合并(15 个文件)
- **涉及文件:** cobol_testgen/cond.py, cobol_testgen/to_sql.py, cobol_testgen/coverage.py, config/program_schema.py 等
- **使用模型:** deepseek
### 2026-08-09 17:41:47 - 配置管理
- **范式步骤:** 项目配置优化
- **修改摘要:** 忽略 --gcow/ 运行时输出目录
- **涉及文件:** .gitignore
- **使用模型:** deepseek
---
## 2026-07-18
### 2026-07-18 08:42:55 - 功能开发阶段
- **范式步骤:** 配置驱动与覆盖率修复
- **修改摘要:** 配置驱动多场景支持 + gcda 累积修复 + gcov 合并修复
- **涉及文件:** cobol_testgen/__init__.py, cobol_testgen/coverage.py, cobol_testgen/to_sql.py, config/program_schema.py, orchestrator.py, orchestrator_db.py
- **使用模型:** deepseek
---
## 2026-07-16
### 2026-07-16 20:21:32 - 文档阶段
- **范式步骤:** 系统集成方案设计
- **修改摘要:** 添加 DB 管线 agent2data 集成计划文档
- **涉及文件:** docs/plans/db-pipeline-agent2data-integration.md
- **使用模型:** deepseek
---
## 2026-07-15
### 2026-07-15 22:23:02 - 文档阶段
- **范式步骤:** 系统分析文档编写
- **修改摘要:** 添加综合系统分析文档
- **涉及文件:** docs/system-analysis.md
- **使用模型:** deepseek
### 2026-07-15 22:08:03 - 文档阶段
- **范式步骤:** 项目文档完善
- **修改摘要:** 添加 README.md,更新 SETUP.mdDB 管线文档)
- **涉及文件:** README.md, SETUP.md
- **使用模型:** deepseek
### 2026-07-15 21:46:28 - 功能开发阶段
- **范式步骤:** Phase2 代码审查修复
- **修改摘要:** Phase2 审查问题修复(12 个文件)
- **涉及文件:** cobol_testgen/core.py, cobol_testgen/coverage.py, cobol_testgen/design.py, cobol_testgen/gcov.py, orchestrator_db.py, runners/gixsql_runner.py 等
- **使用模型:** deepseek
---
## 2026-07-12
### 2026-07-12 21:04:58 - 功能开发阶段
- **范式步骤:** 多轮运行与数据生成器
- **修改摘要:** 多轮运行 + GCOV 合并 + JSON 出力 + DesignDataGenerator25 个文件)
- **涉及文件:** agents/design_data.py, cobol_testgen/data_merger.py, cobol_testgen/gcov.py, orchestrator_db.py, layout/ 目录, rules/pgm_pattern/ 目录等
- **使用模型:** deepseek
---
## 2026-07-11
### 2026-07-11 14:55:52 - 功能开发阶段
- **范式步骤:** DB 管线核心开发
- **修改摘要:** DB 管线补全 + 新增 orchestrator_db/program_schema/to_sql + 清理临时脚本(30 个文件)
- **涉及文件:** cobol_testgen/to_sql.py, config/program_schema.py, orchestrator_db.py, runners/gixsql_runner.py 等
- **使用模型:** deepseek
---
## 2026-07-02
### 2026-07-02 21:26:51 - 功能开发阶段
- **范式步骤:** GCOV 本地化与合并
- **修改摘要:** gcov Windows 本地化 + runner 合并 + 决策覆盖率修复
- **涉及文件:** cobol_testgen/gcov.py, cobol_testgen/runner.py, runners/cobol_runner.py 等
- **使用模型:** deepseek
---
## 2026-06-30
### 2026-06-30 22:14:47 - 功能开发阶段
- **范式步骤:** 解析器增强与文件 I/O
- **修改摘要:** UNSTRING 解析增强 + 跨 FD 数值统一 + 文件 I/O 模块
- **涉及文件:** cobol_testgen/core.py, cobol_testgen/design.py, cobol_testgen/file_io.py, cobol_testgen/output.py, docs/v3-理解文書.md
- **使用模型:** deepseek
---
## 2026-06-25
### 2026-06-25 10:24:15 - 配置管理
- **范式步骤:** 代码审查工具集成
- **修改摘要:** 集成 code-review skill 到项目(14 个文件)
- **涉及文件:** .claude/skills/code-review/SKILL.md, .code-review.yaml, CLAUDE.md 等
- **使用模型:** deepseek
### 2026-06-25 10:20:18 - 修复阶段
- **范式步骤:** 代码审查问题修复
- **修改摘要:** 修复 code review issues #1-#9
- **涉及文件:** cobol_testgen/__init__.py, cobol_testgen/cond.py, cobol_testgen/coverage.py, cobol_testgen/pipeline_bridge.py
- **使用模型:** deepseek
### 2026-06-25 09:53:21 - 测试阶段
- **范式步骤:** 基准测试程序集
- **修改摘要:** 添加 benchmark-programs — 58 个电信 COBOL 测试程序(大量文件)
- **涉及文件:** benchmark-programs/ 目录下所有文件
- **使用模型:** deepseek
### 2026-06-25 08:51:15 - 配置管理
- **范式步骤:** 项目清理
- **修改摘要:** 移除杂散 C 文件,更新 .gitignore
- **涉及文件:** .gitignore, C
- **使用模型:** deepseek
### 2026-06-25 08:50:17 - 文档阶段
- **范式步骤:** 项目文档与脚本更新
- **修改摘要:** SETUP.md + 测试报告脚本 + 文档更新(25 个文件)
- **涉及文件:** SETUP.md, SETUP_QUICK.md, docs/ 目录下多个文档, test-data/ 目录下多个测试脚本
- **使用模型:** deepseek
### 2026-06-25 08:28:43 - 修复阶段
- **范式步骤:** 代码审查修复
- **修改摘要:** code review — 防御性下标处理 + 分支一致化
- **涉及文件:** cobol_testgen/cond.py, cobol_testgen/coverage.py
- **使用模型:** deepseek
---
## 2026-06-24
### 2026-06-24 23:15:08 - 修复阶段
- **范式步骤:** 覆盖率修复
- **修改摘要:** 变量下标匹配 — 43/43 程序 100% 真实分支覆盖
- **涉及文件:** cobol_testgen/cond.py
- **使用模型:** deepseek
### 2026-06-24 23:08:24 - 修复阶段
- **范式步骤:** 覆盖率修复
- **修改摘要:** 真实分支覆盖率 99.9% — 条件解析器全面强化
- **涉及文件:** cobol_testgen/__init__.py, cobol_testgen/cond.py
- **使用模型:** deepseek
### 2026-06-24 22:38:54 - 修复阶段
- **范式步骤:** 覆盖率修复
- **修改摘要:** 真实覆盖率 99% — 移除虚假 fallback + 条件解析器强化
- **涉及文件:** cobol_testgen/__init__.py, cobol_testgen/cond.py, cobol_testgen/coverage.py
- **使用模型:** deepseek
### 2026-06-24 22:14:47 - 修复阶段
- **范式步骤:** 覆盖率修复
- **修改摘要:** 分支覆盖率 100% — 43/43 程序全覆盖
- **涉及文件:** cobol_testgen/__init__.py, cobol_testgen/coverage.py, cobol_testgen/design_mcdc.py
- **使用模型:** deepseek
### 2026-06-24 21:47:10 - 修复阶段
- **范式步骤:** 覆盖率修复
- **修改摘要:** 覆盖率统计 95.6% — __DP 合成约束接入完整管道
- **涉及文件:** cobol_testgen/__init__.py, cobol_testgen/cond.py, cobol_testgen/coverage.py, cobol_testgen/design_mcdc.py
- **使用模型:** deepseek
### 2026-06-24 21:14:50 - 修复阶段
- **范式步骤:** 覆盖率修复
- **修改摘要:** 覆盖率统计全面修复 + 5 漏洞修正
- **涉及文件:** cobol_testgen/__init__.py, cobol_testgen/cond.py, cobol_testgen/coverage.py, cobol_testgen/design_mcdc.py, cobol_testgen/pipeline_bridge.py, cobol_testgen/procedure_parser.py
- **使用模型:** deepseek
---
## 2026-06-23
### 2026-06-23 22:38:17 - 功能开发阶段
- **范式步骤:** 本地改进合并
- **修改摘要:** merge local cobol_testgen improvements into v3 shared modules
- **涉及文件:** cobol_testgen/__init__.py, cobol_testgen/cond.py, cobol_testgen/core.py, cobol_testgen/coverage.py, cobol_testgen/design.py, cobol_testgen/gcov.py, cobol_testgen/grammar.lark, cobol_testgen/output.py, cobol_testgen/read.py
- **使用模型:** deepseek
---
## 2026-06-22
### 2026-06-22 23:41:22 - 功能开发阶段
- **范式步骤:** 基准程序全量解析
- **修改摘要:** 37/37 基准程序全量解析 + O(N) 路径枚举 + 运行时 gcov 验证(16 个文件)
- **涉及文件:** cobol_testgen/core.py, cobol_testgen/flatfile.py, cobol_testgen/grammar.lark, test-data/s16_benchmark_e2e.py 等
- **使用模型:** deepseek
### 2026-06-22 13:59:54 - 修复阶段
- **范式步骤:** Bug 修复
- **修改摘要:** 溢出截断 + flatfile 字段路由 + 多 E2E 验证
- **涉及文件:** cobol_testgen/design.py, cobol_testgen/flatfile.py
- **使用模型:** deepseek
### 2026-06-22 13:52:56 - 修复阶段
- **范式步骤:** Bug 修复
- **修改摘要:** 跨文件 KEY 约束 + PERFORM 分支统计 + 平面文件写入
- **涉及文件:** cobol_testgen/__init__.py, cobol_testgen/flatfile.py
- **使用模型:** deepseek
### 2026-06-22 13:30:28 - 测试阶段
- **范式步骤:** 端到端验证
- **修改摘要:** 覆盖率测量端到端验证(17 测试/全通过)
- **涉及文件:** test-data/s15_coverage_verification.py
- **使用模型:** deepseek
### 2026-06-22 13:18:07 - 修复阶段
- **范式步骤:** Grammar 增强与回归验证
- **修改摘要:** classification 修复 + grammar 增强 + 75/75 回归确认
- **涉及文件:** cobol_testgen/grammar.lark, cobol_testgen/read.py, hina/pipeline/pipeline.py, hina/rule_engine/confusion_groups.py
- **使用模型:** deepseek
### 2026-06-22 12:31:00 - 测试阶段
- **范式步骤:** 基准测试套件
- **修改摘要:** 58-program benchmark suite — Lark grammar fixes + external COBOL validation
- **涉及文件:** cobol_testgen/grammar.lark, cobol_testgen/read.py, test-data/s14_benchmark_suite.py
- **使用模型:** deepseek
### 2026-06-22 11:36:33 - 修复阶段
- **范式步骤:** 审计修复
- **修改摘要:** 3 bugs confirmed and repaired from honest audit
- **涉及文件:** cobol_testgen/core.py, cobol_testgen/design.py, test-data/s13_honest_audit.py
- **使用模型:** deepseek
### 2026-06-22 10:49:18 - 测试阶段
- **范式步骤:** 漏洞评审
- **修改摘要:** 专家漏洞评审 — 发现并修复嵌套 COPYBOOK 解析 bug
- **涉及文件:** cobol_testgen/read.py, test-data/r16_vuln_review.py
- **使用模型:** deepseek
### 2026-06-22 10:38:51 - 测试阶段
- **范式步骤:** 用户故事验收测试
- **修改摘要:** Role-based user stories — 23 acceptance criteria, 43 tests
- **涉及文件:** test-data/s12_role_user_stories.py
- **使用模型:** deepseek
### 2026-06-22 10:31:53 - 测试阶段
- **范式步骤:** 迁移风险测试
- **修改摘要:** COBOL->Java migration risk test — 14 risk areas, 30 real COBOL compiles
- **涉及文件:** test-data/s11_migration_risk_test.py
- **使用模型:** deepseek
### 2026-06-22 10:11:06 - 测试阶段
- **范式步骤:** 覆盖率补充
- **修改摘要:** fill remaining coverage gaps — 55 tests, 83% line coverage
- **涉及文件:** test-data/r15_fill_gaps.py
- **使用模型:** deepseek
### 2026-06-22 09:59:44 - 测试阶段
- **范式步骤:** 覆盖率补充
- **修改摘要:** fill coverage gaps — parametrized, comparator, jcl, storage
- **涉及文件:** test-data/r14_coverage_gaps.py
- **使用模型:** deepseek
### 2026-06-22 09:37:58 - 测试阶段
- **范式步骤:** 最终扫描
- **修改摘要:** final sweep — EXEC stripping + INSPECT bugfix + more EQ assertions
- **涉及文件:** cobol_testgen/core.py, cobol_testgen/read.py, test-data/r12_real_cobol_pipeline.py, test-data/r13_final_sweep.py
- **使用模型:** deepseek
### 2026-06-22 09:22:39 - 测试阶段
- **范式步骤:** 真实 COBOL 样本测试
- **修改摘要:** 72 个真实 COBOL 样本全量管道测试 + 端到端验证
- **涉及文件:** test-data/r12_real_cobol_pipeline.py, test-data/r12b_orchestrator_e2e.py
- **使用模型:** deepseek
### 2026-06-22 09:10:21 - 修复阶段
- **范式步骤:** 约束修复
- **修改摘要:** generate_data constraint steering fully repaired
- **涉及文件:** cobol_testgen/core.py, test-data/r11_real_verification.py
- **使用模型:** deepseek
### 2026-06-22 00:32:23 - 测试阶段
- **范式步骤:** 真实验证测试
- **修改摘要:** real verification tests (55 tests, falsifiable assertions)
- **涉及文件:** test-data/r11_real_verification.py
- **使用模型:** deepseek
### 2026-06-22 00:20:41 - 测试阶段
- **范式步骤:** 分支覆盖测试
- **修改摘要:** pipeline.py(32IF) + hina_agent.py(12IF) 分歧完全網羅
- **涉及文件:** test-data/r10_pipeline_agent.py
- **使用模型:** deepseek
### 2026-06-22 00:17:42 - 测试阶段
- **范式步骤:** 深层覆盖测试
- **修改摘要:** read.py 残り 54IF 深層 + pipeline/agent 補完(76 テスト)
- **涉及文件:** test-data/r9_deep_coverage.py
- **使用模型:** deepseek
### 2026-06-22 00:11:24 - 测试阶段
- **范式步骤:** 环境依赖测试
- **修改摘要:** 环境依赖模块真实测试(cobc/Java/FastAPI/gcov43/43
- **涉及文件:** test-data/r8_env_coverage.py
- **使用模型:** deepseek
### 2026-06-22 00:02:18 - 测试阶段
- **范式步骤:** 全模块深层覆盖
- **修改摘要:** 全モジュール深層カバレッジ補完(727テスト/0FAIL)
- **涉及文件:** test-data/r4_cond_coverage.py, test-data/r4_coverage_coverage.py, test-data/r4_deep_coverage.py, test-data/r4_design_coverage.py, test-data/r5_integration_coverage.py, test-data/r6_deep_coverage.py, test-data/r7_final_deep.py
- **使用模型:** deepseek
---
## 2026-06-19
### 2026-06-19 23:51:55 - 功能开发阶段
- **范式步骤:** Phase 2 完成
- **修改摘要:** Phase 2 complete — 13 Phases of COBOL type classification and test benchmark(大量文件重构与测试补充)
- **涉及文件:** `cobol_testgen/`, `hina/`, `tests/`, `config/`, `coverage/`, `parametrized/` 等多个模块
- **使用模型:** deepseek
---
## 2026-06-18
### 2026-06-18 17:31:16 - 修复阶段
- **范式步骤:** LLM 解析修复 + 覆盖率补充
- **修改摘要:** _parse_llm_response 现在能优雅处理空/无效 JSON;添加 gap coverage tests
- **涉及文件:** `hina/hina_agent.py`, `test-data/test_gap_coverage.py`
- **使用模型:** deepseek
### 2026-06-18 17:27:19 - 测试阶段
- **范式步骤:** AI Agent 合规验证
- **修改摘要:** AI Agent v6 node compliance validation (6 nodes, 24/24)
- **涉及文件:** `test-data/test_ai_flow_compliance.py`
- **使用模型:** deepseek
### 2026-06-18 17:21:12 - 测试阶段
- **范式步骤:** 深度验证套件
- **修改摘要:** deep validation suite (real COBOL/HINA/QG/retry/report/perf - 28/28)
- **涉及文件:** `test-data/test_deep_validation.py`
- **使用模型:** deepseek
### 2026-06-18 17:17:11 - 测试阶段
- **范式步骤:** 主验证套件
- **修改摘要:** master validation suite (Pipeline/HINA/Benchmark/QG/Retry/Report - 30/30)
- **涉及文件:** `test-data/test_master_validation.py`
- **使用模型:** deepseek
### 2026-06-18 17:10:40 - 测试阶段
- **范式步骤:** 用户故事测试
- **修改摘要:** platform user story tests (43/43, 4 categories)
- **涉及文件:** `report/generator.py`, `test-data/test_platform_user_stories.py`
- **使用模型:** deepseek
### 2026-06-18 17:05:51 - 测试阶段
- **范式步骤:** 综合测试计划
- **修改摘要:** comprehensive test plan and auto test runner (20/20 passed, 100%)
- **涉及文件:** `docs/test-plan.md`, `test-data/run_all_tests.py`
- **使用模型:** deepseek
### 2026-06-18 16:55:43 - 测试阶段
- **范式步骤:** HINA 类型测试数据
- **修改摘要:** HINA type-specific COBOL test data suite (10 programs, 8/10 pass)
- **涉及文件:** `test-data/cobol/HINA*.cbl`, `test-data/run_validation.py`
- **使用模型:** deepseek
### 2026-06-18 16:47:21 - 修复阶段
- **范式步骤:** 多模块修复
- **修改摘要:** P1 - complete_tests feeds DataWriter; P2 - loop syncs complete_tests; P5 - machine_json gets coverage fields
- **涉及文件:** `orchestrator.py`, `report/generator.py`
- **使用模型:** deepseek
### 2026-06-18 16:31:54 - 功能开发阶段
- **范式步骤:** Phase 3+4 开发
- **修改摘要:** gcov support + enhanced report
- **涉及文件:** `hina/gcov_collector.py`, `report/generator.py`, `runners/cobol_runner.py`
- **使用模型:** deepseek
### 2026-06-18 16:26:44 - 修复阶段
- **范式步骤:** 真实 COBOL 验证修复
- **修改摘要:** 3 issues found during real COBOL validation
- **涉及文件:** `cobol_testgen/read.py`, `hina/classifier.py`
- **使用模型:** deepseek
### 2026-06-18 16:10:38 - 功能开发阶段
- **范式步骤:** Phase 2 开发
- **修改摘要:** HINA Agent + Strategy Agent + classifier
- **涉及文件:** `hina/classifier.py`, `hina/hina_agent.py`, `hina/strategy.py`, `orchestrator.py`
- **使用模型:** deepseek
### 2026-06-18 16:02:38 - 功能开发阶段
- **范式步骤:** Phase 1 完成
- **修改摘要:** orchestrator quality gate loop + hina/gate + main CLI args
- **涉及文件:** `hina/gate.py`, `main.py`, `orchestrator.py`
- **使用模型:** deepseek
### 2026-06-18 15:47:35 - 功能开发阶段
- **范式步骤:** Phase 1 开始
- **修改摘要:** cobol_testgen API + quality fields + retry handler
- **涉及文件:** `cobol_testgen/__init__.py`, `cobol_testgen/coverage.py`, `config/__init__.py`, `data/diff_result.py`, `hina/__init__.py`, `hina/retry.py`
- **使用模型:** deepseek
---
## 2026-06-10
### 2026-06-10 22:56:22 - 功能开发阶段
- **范式步骤:** 语句支持完善
- **修改摘要:** complete INSPECT/SEARCH support, fix PERFORM/EVAL coverage marking
- **涉及文件:** `cobol_testgen/__init__.py`, `cobol_testgen/agents.py`, `cobol_testgen/core.py`, `cobol_testgen/coverage.py`, `cobol_testgen/design.py`, `cobol_testgen/read.py`, `AGENTS.md`
- **使用模型:** deepseek
---
## 2026-06-08
### 2026-06-08 21:07:16 - 功能开发阶段
- **范式步骤:** cobol_testgen 模块初始化
- **修改摘要:** add cobol_testgen module
- **涉及文件:** `cobol_testgen/__init__.py`, `cobol_testgen/__main__.py`, `cobol_testgen/agents.py`, `cobol_testgen/cond.py`, `cobol_testgen/core.py`, `cobol_testgen/coverage.py`, `cobol_testgen/design.py`, `cobol_testgen/grammar.lark`, `cobol_testgen/read.py`
- **使用模型:** deepseek
---
## 2026-05-27
### 2026-05-27 08:42:41 - 功能开发阶段
- **范式步骤:** V3 平台初始化
- **修改摘要:** cobol-java migration verification platform v3 (42 tests, JCL module)
- **涉及文件:** `DESIGN.md`, `config.py`, `jcl/__init__.py`, `jcl/executor.py`, `jcl/parser.py`, `orchestrator.py`, `tests/test_golden.py`
- **使用模型:** deepseek
---
## 2026-05-24
### 2026-05-24 13:01:31 - 测试阶段
- **范式步骤:** 边界用例测试
- **修改摘要:** add edge case tests
- **涉及文件:** `tests/comparator/test_aligner_edge.py`, `tests/comparator/test_compare_edge.py`
- **使用模型:** deepseek
### 2026-05-24 12:52:20 - 功能开发阶段
- **范式步骤:** Web 层开发
- **修改摘要:** add web layer (FastAPI + worker)
- **涉及文件:** `web/__init__.py`, `web/api.py`, `web/static/script.js`, `web/static/style.css`, `web/templates/result.html`, `web/templates/upload.html`, `web/worker.py`, `requirements.txt`
- **使用模型:** deepseek
### 2026-05-24 12:36:44 - 功能开发阶段
- **范式步骤:** 代码生成初始化
- **修改摘要:** v3: gstack-code-gen 生成
- **涉及文件:** `agents/`, `comparator/`, `config/`, `data/`, `main.py`, `orchestrator.py`, `preprocessor.py`, `quality/`, `report/`, `runners/`, `storage/`, `tests/` 等全部初始模块
- **使用模型:** deepseek
---
## 2026-06-21
### 2026-06-21 23:09:07 - 测试阶段
- **范式步骤:** 深层覆盖测试
- **修改摘要:** 深層カバレッジ補完 — 23/23 通過
- **涉及文件:** test-data/round3_deep_coverage.py
- **使用模型:** deepseek
### 2026-06-21 22:56:19 - 测试阶段
- **范式步骤:** 全模块覆盖测试
- **修改摘要:** 40/40 覆盖 parametrized/division + 全 comparator + jcl/executor + agents + runners + report
- **涉及文件:** test-data/round2_remaining_tests.py
- **使用模型:** deepseek
### 2026-06-21 22:16:21 - 测试阶段
- **范式步骤:** 模块覆盖测试
- **修改摘要:** 残り 20 モジュール全カバー (84/84 PASS)
- **涉及文件:** test-data/test_remaining_modules.py
- **使用模型:** deepseek
### 2026-06-21 21:53:30 - 测试阶段
- **范式步骤:** 分支覆盖测试
- **修改摘要:** 164/164 全分支全覆盖 — 10 モジュール × 178IF
- **涉及文件:** docs/coverage-matrix-final.md, test-data/test_branch_coverage.py, tests/test_jcl.py
- **使用模型:** deepseek
---
## 统计摘要
| 指标 | 数值 |
|------|------|
| **总提交数** | 82 |
| **时间跨度** | 2026-05-24 ~ 2026-08-09 |
| **使用模型** | DeepSeek |
| **主要阶段** | 测试 → 修复 → 功能开发 → 文档 |
| 阶段 | 提交数 | 占比 |
|------|--------|------|
| 测试阶段 | 33 | 40% |
| 修复阶段 | 19 | 23% |
| 功能开发阶段 | 22 | 27% |
| 文档阶段 | 5 | 6% |
| 配置管理 | 3 | 4% |
### 2026-08-22 17:00:00 - 文档
- **范式步骤:** 详细设计文档编写
- **修改摘要:** 创建 cobol_testgen 核心引擎模块的详细设计文档,包含模块概述、文件清单、核心数据结构、解析流程、分支树构建、条件解析、路径枚举、值生成、覆盖率分析、输出生成、接口定义、错误处理共12个章节
- **涉及文件:** docs/detailed-design/01-cobol-testgen-core.md
- **使用模型:** deepseek
### 2026-08-22 17:15:00 - 文档
- **范式步骤:** 详细设计文档编写
- **修改摘要:** 创建 DB 管道编排器 (orchestrator_db.py) 的详细设计文档,包含模块概述、核心数据结构、6步流程设计、接口定义、数据流、错误处理、性能设计共8个章节
- **涉及文件:** docs/detailed-design/02-orchestrator-db.md
- **使用模型:** deepseek
### 2026-08-22 17:30:00 - 文档
- **范式步骤:** 详细设计文档编写
- **修改摘要:** 创建编译运行引擎 (runners) 的详细设计文档,包含模块概述、文件清单、接口定义(3个Runner+DataWriter)、编译流程(COBOL/DB COBOL/Java)、运行流程(stdin管道/文件I/O/Spark)、错误处理、设计特点、依赖关系共8个章节
- **涉及文件:** docs/detailed-design/03-runners.md
- **使用模型:** deepseek
### 2026-08-22 21:00:00 - 文档阶段
- **范式步骤:** LLM代理模块、比对模块、配置系统详细设计文档编写
- **修改摘要:** 创建三个详细设计文档:05-agents-llm.mdLLM代理模块,包含Agent1/2/3、DesignDataGenerator、LLMClient等6个组件)、06-comparator.md(比对模块,包含记录对齐、二进制读取、数据标准化、字段比对、舍入检测5个组件)、07-config-system.md(配置系统,包含两级配置体系、TOML/YAML加载、程序Schema定义)
- **涉及文件:** `docs/detailed-design/05-agents-llm.md`, `docs/detailed-design/06-comparator.md`, `docs/detailed-design/07-config-system.md`
- **使用模型:** deepseek
### 2026-08-22 20:00:00 - 文档
- **范式步骤:** 编写详细设计文档 08-data-flow.md
- **修改摘要:** 创建 COBOL 迁移验证平台 V3 数据流设计文档,覆盖非DB管道和DB管道的完整数据流、核心数据结构、Mermaid 流程图、跨模块数据传递关系
- **涉及文件:** docs/detailed-design/08-data-flow.md
- **使用模型:** deepseek
+324
View File
@@ -0,0 +1,324 @@
# V3系统总体设计
> 版本: v1.0 | 日期: 2026-08-22
> 本文档描述COBOL迁移验证平台V3的总体架构和模块设计。
---
## 一、系统概述
### 1.1 系统定位
COBOL迁移验证平台V3是一个AI辅助的自动化测试工具,用于验证COBOL程序向Java/Spark迁移的正确性。
### 1.2 核心能力
| 能力 | 说明 |
|------|------|
| COBOL源码解析 | 自动解析DATA DIVISION和PROCEDURE DIVISION |
| 测试数据生成 | 基于分支覆盖的测试数据自动生成 |
| 双管道验证 | 支持非DBflat file)和DBSQLite)两种验证模式 |
| 覆盖率分析 | 静态分支覆盖 + 动态gcov覆盖 |
| AI辅助 | LLM驱动的程序分类和测试策略生成 |
### 1.3 技术栈
| 组件 | 技术 |
|------|------|
| 语言 | Python 3.12+ |
| 解析器 | Lark (Earley parser) |
| COBOL编译 | GnuCOBOL 3.2.0 |
| DB管道 | gixsql + SQLite |
| AI模型 | DeepSeek |
| 测试框架 | pytest |
---
## 二、系统架构
### 2.1 架构图
```
┌─────────────────────────────────────────────────────────────────┐
│ V3系统架构 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ CLI入口 │ │ Web界面 │ │ API接口 │ │
│ │ main.py │ │ web/ │ │ __init__ │ │
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │ │
│ └────────────────────┼────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 编排层 (Orchestrator) │ │
│ │ orchestrator.py (非DB) orchestrator_db.py (DB) │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌────────────────────┼────────────────────┐ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ 核心引擎 │ │ 运行引擎 │ │ AI代理 │ │
│ │ cobol_testgen│ │ runners/ │ │ agents/ │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │ │ │ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ 比对模块 │ │ 报告模块 │ │ 配置模块 │ │
│ │ comparator/ │ │ report/ │ │ config/ │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
```
### 2.2 分层架构
| 层级 | 模块 | 职责 |
|------|------|------|
| **L1 数据层** | data/ | 共享数据模型 |
| **L2 核心层** | cobol_testgen/, config/ | COBOL解析、配置管理 |
| **L3 业务层** | hina/, agents/, comparator/ | 分类、AI代理、比对 |
| **L4 编排层** | orchestrator*, runners/ | 流程编排、执行 |
| **L5 接口层** | main.py, web/, __init__.py | 用户接口 |
---
## 三、模块清单
### 3.1 核心模块
| 模块 | 文件数 | 行数 | 职责 |
|------|--------|------|------|
| cobol_testgen/ | 22 | ~8000 | COBOL解析、测试数据生成 |
| orchestrator_db.py | 1 | 1334 | DB管道6步编排 |
| runners/ | 8 | ~600 | 编译运行引擎 |
| hina/ | 11 | ~2000 | HINA程序分类 |
| agents/ | 6 | ~800 | LLM代理 |
| comparator/ | 6 | ~400 | 字段比对 |
| config/ | 5 | ~300 | 配置管理 |
| report/ | 1 | ~200 | 报告生成 |
### 3.2 模块依赖关系
```
models.py (零依赖)
├── read.py (lark)
├── cond.py (stdlib)
├── core.py (cond)
├── design.py (models, cond, core)
├── coverage.py (models, cond)
├── output.py (file_io)
├── to_sql.py (stdlib)
├── runner.py (file_io)
├── flatfile.py (read, file_io)
└── __init__.py (所有上层模块)
```
---
## 四、数据流
### 4.1 非DB管道数据流
```
COBOL源码
read.py: preprocess() + parse_data_division()
core.py: build_branch_tree()
design.py: enum_paths() + generate_records()
output.py: output_json() + output_input_files()
runners/cobol_runner.py: compile() + run()
comparator/: compare_field()
report/generator.py: generate_report()
```
### 4.2 DB管道数据流
```
COBOL源码 (含EXEC SQL)
gixsql_runner.py: preprocess() + compile()
orchestrator_db.py: step2_generate_inputs()
├── cobol_testgen: extract_structure + generate_data
├── to_sql.py: build_db_input()
└── flatfile.py: write_all_files()
orchestrator_db.py: step3_run_cobol()
orchestrator_db.py: step4_extract_intermediate()
orchestrator_db.py: step5_run_java() (可选)
orchestrator_db.py: step6_verify()
```
---
## 五、接口设计
### 5.1 公开API
```python
# cobol_testgen/__init__.py
def extract_structure(
cobol_source: str,
copybook_dirs: list[str] = None
) -> dict:
"""解析COBOL源码,返回结构信息"""
pass
def generate_data(
cobol_source: str,
structure: dict,
copybook_dirs: list[str] = None
) -> list[dict]:
"""生成分支覆盖测试数据"""
pass
def main():
"""CLI入口"""
pass
```
### 5.2 配置接口
```python
# config/__init__.py
@dataclass
class Config:
"""全局配置"""
project_name: str
copybook_paths: list[str]
dialect: str
llm_model: str
gcov_enabled: bool
# ... 更多字段
```
---
## 六、错误处理
### 6.1 错误分类
| 类别 | 示例 | 处理策略 |
|------|------|----------|
| 解析错误 | COBOL语法不合法 | 抛出异常,返回错误信息 |
| 编译错误 | cobc编译失败 | 记录日志,跳过该程序 |
| 运行错误 | 程序执行异常 | 捕获异常,标记失败 |
| 超时错误 | 执行超时 | 强制终止,记录超时 |
### 6.2 容错机制
- **解析器超时**pipeline_bridge 3秒超时回退
- **LLM失败**:回退到规则引擎
- **gcov失败**:跳过覆盖率收集
- **DB连接失败**:重试或跳过
---
## 七、性能设计
### 7.1 性能指标
| 指标 | 目标 |
|------|------|
| 单程序解析 | < 5秒 |
| 测试数据生成 | < 30秒 |
| 编译运行 | < 60秒 |
| 覆盖率报告 | < 10秒 |
### 7.2 优化策略
- **路径枚举**O(N)线性算法替代O(2^N)
- **并行执行**:多场景gcov并行收集
- **缓存机制**LLM结果缓存
- **增量补充**:质量门循环最多4次
---
## 八、安全设计
### 8.1 输入验证
- COBOL源码:长度限制、编码检查
- 配置文件:YAML schema验证
- 文件路径:路径遍历防护
### 8.2 资源限制
- LLM调用:最大成本限制
- 执行时间:超时强制终止
- 内存使用:大文件分块处理
---
## 九、测试策略
### 9.1 测试层次
| 层次 | 覆盖率目标 | 工具 |
|------|------------|------|
| 单元测试 | ≥ 90% | pytest |
| 集成测试 | ≥ 80% | pytest |
| 端到端测试 | 100%通过 | 自定义脚本 |
### 9.2 测试数据
- 43个COBOL基准程序
- 33+2种程序类型
- 58个电信测试程序
---
## 十、部署设计
### 10.1 运行环境
| 组件 | 要求 |
|------|------|
| OS | Windows 10/11 |
| Python | 3.12+ |
| GnuCOBOL | 3.2.0 |
| 磁盘 | ≥ 500MB |
### 10.2 安装步骤
```bash
# 1. 安装Python依赖
pip install lark pathlib pyyaml
# 2. 安装GnuCOBOL
# 下载GC32-BDB-SP1,添加到PATH
# 3. 验证安装
python -c "from cobol_testgen import extract_structure; print('OK')"
```
@@ -0,0 +1,581 @@
# cobol_testgen 核心引擎模块 - 详细设计文档
> 模块路径: `cobol_testgen/`
> 版本: V3 (2026技术大赛)
> 依赖: Python 3.13+, `lark>=1.1.0`
---
## 1. 模块概述
### 1.1 职责
`cobol_testgen` 是 COBOL 迁移验证平台 V3 的核心引擎, 负责:
1. **解析** COBOL 源码 (DATA DIVISION + PROCEDURE DIVISION)
2. **构建** 分支控制流树 (Branch Tree)
3. **枚举** 所有可达路径 (路径级约束)
4. **生成** 满足路径约束的测试数据记录
5. **输出** JSON 格式测试数据 + HTML 覆盖率报告
### 1.2 边界
| 在范围内 | 不在范围内 |
|---------|-----------|
| COBOL 源码静态分析 | COBOL 程序动态执行 (由 runner.py 调用 GnuCOBOL) |
| 分支树构建 + 路径枚举 | LLM 推理 (由外部 deepseek API 调用) |
| 测试数据值生成 | SQL 数据库操作 (to_sql.py 辅助生成 DB 输入行) |
| JSON/HTML 输出 | IDE 集成、CI/CD 管道 |
### 1.3 依赖关系
```
cobol_testgen/
__init__.py <- 公开 API 入口 (main)
models.py <- 共享数据模型 (零外部依赖)
read.py <- INPUT 层: 预处理 + DATA DIVISION 解析
core.py <- CORE 层: PROCEDURE DIVISION 分支树构建
procedure_parser.py <- CORE 层: 行级状态机解析器 (新)
pipeline_bridge.py <- CORE 层: 新旧解析器桥接
cond.py <- CONDITION 层: 条件解析 + MC/DC
design.py <- DESIGN 层: 路径枚举 + 值生成
design_mcdc.py <- DESIGN 层: O(N) 非爆炸路径枚举
coverage.py <- COVERAGE 层: 分支覆盖标记 + HTML 报告
output.py <- OUTPUT 层: JSON 输出
to_sql.py <- SQL 辅助: WHERE 约束解析 + DB 输入行生成
flatfile.py <- I/O 辅助: 固定长度平面文件读写
file_io.py <- I/O 辅助: DISPLAY/COMP/COMP-3 二进制编解码
runner.py <- 执行层: 编译-执行-验证 (调用 GnuCOBOL)
gcov.py <- 覆盖率辅助: gcov 数据解析
data_merger.py <- 数据整合: 白盒+机能+策略数据合并
grammar.lark <- Lark 语法: DATA DIVISION 解析
procedure_grammar.lark <- Lark 语法: PROCEDURE DIVISION 解析
__main__.py <- 入口: python -m cobol_testgen
```
**外部依赖:**
| 依赖 | 用途 |
|------|------|
| lark>=1.1.0 | Lark 解析器框架 (Earley parser, dynamic lexer) |
| deepseek API | LLM 路径生成 (可选, 回退到规则引擎) |
| GnuCOBOL (cobc) | COBOL 编译-执行 (runner.py 调用) |
| gcov | 代码覆盖率采集 (gcov.py 调用) |
---
## 2. 文件清单
| 文件 | 行数 | 职责 |
|------|------|------|
| `__init__.py` | 2189 | 公开 API 入口、OCCURS 展开、PREV 连锁、跨文件键值协调、MERGE/SORT/LINAGE 注入、子程序输入供给 |
| `__main__.py` | 3 | python -m cobol_testgen 入口 |
| `models.py` | 118 | 共享数据模型: PicInfo, FieldDef, BrSeq, BrIf, BrEval, BrPerform, BrSearch, CondLeaf, Assign, CallNode, ExitNode, GoTo, ParseError, ProcParseResult |
| `read.py` | 644 | COBOL 源码预处理 (fixed/free format)、COPYBOOK 展开、DATA DIVISION 解析 (Lark grammar.lark)、FILE-CONTROL 解析、OPEN 语句扫描 |
| `core.py` | 2137 | PROCEDURE DIVISION 分支树构建 (_BrParser 类)、段落扫描、赋值追踪、SQL 虚拟字段注册、算术表达式解析、EVALUATE/PERFORM/SEARCH/READ/WRITE 解析 |
| `procedure_parser.py` | 527 | 新版行级状态机解析器 (Tier 1: 状态机提取嵌套结构, Tier 2: 规则条件解析) |
| `pipeline_bridge.py` | 225 | 新旧解析器桥接: 新解析器优先, 旧解析器 3s 超时回退 |
| `cond.py` | 497 | COBOL 条件表达式解析 (AND/OR/NOT/括号)、MC/DC 约束集生成 (mcdc_sets)、满足值计算 (satisfying_value) |
| `design.py` | 2014 | 路径枚举 (enum_paths)、基础记录生成 (make_base_record)、约束应用 (apply_constraint)、赋值传播 (propagate_assignments)、链追溯 (trace_to_root) |
| `design_mcdc.py` | 409 | O(N) 非爆炸路径枚举: 每个决策点生成 T/F 两条路径, 保证全覆盖无指数爆炸 |
| `coverage.py` | 1451 | 决策点收集 (collect_decision_points)、分支覆盖标记 (mark_coverage)、中文 HTML 报告生成 |
| `output.py` | 184 | JSON 输出: 按 FD 分组 input/expected_output/working_storage |
| `to_sql.py` | 948 | SQL 元数据提取 (collect_sql_meta)、WHERE 宿主变量 MOVE 链解析、DB 输入行生成 (build_db_input) |
| `flatfile.py` | 309 | 平面文件 I/O: FD 布局分析 (analyze_fd_layout)、固定长度记录读写 |
| `file_io.py` | 234 | 二进制编解码: DISPLAY/COMP/COMP-3 pack/unpack、RECORDING MODE V 处理 |
| `runner.py` | 516 | 编译-执行-验证: GnuCOBOL cobc 调用、字段对比、分组执行 |
| `gcov.py` | 165 | gcov 覆盖率数据解析 (.cbl.gcov -> {行号: 执行次数}) |
| `data_merger.py` | 130 | 数据整合: 白盒 (MC/DC) + 机能 (LLM) + 策略 (HINA 分类) 数据合并去重 |
| `grammar.lark` | 40 | Lark 语法: DATA DIVISION 解析 |
| `procedure_grammar.lark` | 203 | Lark 语法: PROCEDURE DIVISION 解析 |
---
---
## 3. 核心数据结构
所有模型定义在 `models.py` (118 行), 无外部依赖。
### 3.1 字段定义
```python
@dataclass
class PicInfo:
type: str = 'unknown' # "numeric" | "alphanumeric" | "alphabetic"
digits: int = 0 # 整数位数 (如 9(7) -> 7)
decimal: int = 0 # 小数位数 (如 V99 -> 2)
length: int = 0 # 总长度 (alphanumeric 用)
signed: bool = False # 是否有符号
@dataclass
class FieldDef:
name: str # 字段名 (大写)
level: int # 层号 (01, 05, 77, 88 等)
pic: str | None = None # PIC 子句原始文本
pic_info: PicInfo | None = None
is_filler: bool = False
occurs_count: int = 0
occurs_depending: str | None = None
redefines: str | None = None
usage: str | None = None # "COMP" | "COMP-3" | "BINARY" | "DISPLAY"
value: str | None = None
values: list[str] | None = None # 88 级多值
is_88: bool = False
parent: str | None = None
section: str | None = None
```
### 3.2 分支树节点
```python
class BrSeq: # 顺序语句序列 (容器节点)
children = [] # BrIf | BrEval | BrPerform | BrSearch | Assign | ...
class BrIf: # IF 条件分支
condition # 条件原文
cond_tree = None # 条件树 (core.py 解析时赋值)
true_seq # THEN 分支
false_seq # ELSE 分支
class BrEval: # EVALUATE 多分支
subject # 主体
subjects = [] # ALSO 多主体
when_list = [] # [(condition_text, BrSeq)]
other_seq # WHEN OTHER
has_other = False
class BrPerform: # PERFORM 循环/调用
perf_type # "until" | "varying" | "times" | "para" | "sort"
condition # UNTIL 条件
body_seq # 循环体
class BrSearch: # SEARCH 表查找
table_name, is_all, at_end_seq, when_list, has_at_end
```
### 3.3 条件树
```python
class CondLeaf: field, op, value # 叶条件
class CondNot: child # NOT 取反
class CondAnd: left, right # AND
class CondOr: left, right # OR
```
### 3.4 其他节点
```python
class Assign: target, source_info # 赋值 (MOVE/COMPUTE/READ INTO/WRITE FROM)
class CallNode: program_name, using_params # CALL 子程序
class GoTo: target, body_seq # GO TO 跳转
class ExitNode: exit_type # EXIT 退出
```
### 3.5 约束与路径
```python
Constraint = tuple # (field, op, value, want_true)
Path = list[Constraint]
class ParseError: line, message, severity
class ProcParseResult: tree, assignments, errors, fallback_to_ai
```
---
## 4. 解析流程
### 4.1 源码预处理 (read.py)
入口: `preprocess(source, extra_search_paths)`
1. COPYBOOK 展开 -> EXEC SQL/CICS 移除 -> VALUE 逗号清理
2. & 连接行合并 -> PIC 小数点转换 -> 格式检测 (fixed/free)
3. 注释移除 -> 续行合并
### 4.2 DATA DIVISION 解析 (read.py)
使用 `grammar.lark` (Lark Earley parser)。关键: Earley parser 处理歧义语法, 命名终端 USAGE_VAL 避免 Lark 过滤 tree children。
### 4.3 PROCEDURE DIVISION 分支树 (core.py)
入口: `build_branch_tree(proc_text, fields, full_source)`
段落扫描 (`scan_paragraphs`) -> 分支解析 (`_BrParser` 递归下降) -> 赋值追踪 (`assignments` 字典)。
### 4.4 新版解析器 (procedure_parser.py + pipeline_bridge.py)
Tier 1: 行级状态机提取嵌套结构 -> Tier 2: 规则条件解析。桥接: 新解析器优先, 旧解析器 3s 超时回退。
---
## 5. 分支树构建细节 (core.py)
### 5.1 流程
```
输入: proc_text, fields
1. raw_lines = proc_text.split('\n')
2. blocked_names = 收集数据名, 阻止误匹配段落名
3. paragraphs = scan_paragraphs(raw_lines, blocked_names)
4. parser = _BrParser(filtered, paragraphs, raw_lines, assignments, fields)
5. tree = parser.parse_seq(terminators={'GOBACK', 'STOP RUN', 'EXIT PROGRAM'})
```
### 5.2 _BrParser 支持的语句
IF -> EVALUATE -> PERFORM -> SEARCH -> READ/WRITE -> CALL -> GO TO -> MOVE/COMPUTE/ADD/SUBTRACT/MULTIPLY/DIVIDE -> SORT/MERGE -> EXEC SQL -> INITIALIZE -> STRING/UNSTRING
### 5.3 IF 解析
提取条件 -> parse_compound_condition -> 递归 parse_seq (THEN) -> 递归 parse_seq (ELSE) -> 消费 END-IF -> 返回 BrIf + cond_tree
### 5.4 EVALUATE 解析
提取主体 -> 检测 ALSO -> 循环解析 WHEN (条件 + 递归体) -> WHEN OTHER -> 返回 BrEval
---
## 6. 条件解析 (cond.py)
### 6.1 单条件 (parse_single_condition)
| 模式 | 示例 | 返回 |
|------|------|------|
| 标准比较 | `AMOUNT > 1000` | `('AMOUNT', '>', '1000')` |
| 88 级 | `STATUS-APPROVED` | `(parent, '=', value)` |
| NOT | `X NOT = 5` | `('X', '<>', '5')` |
| 裸字段 | `WS-EOF` | `('WS-EOF', '=', 'Y')` |
| SQLCODE | `SQLCODE = 100` | `('SQLCODE', '=', '100')` |
| Class | `WS-KEY IS NUMERIC` | `('WS-KEY', 'IS', 'NUMERIC', True)` |
| FUNCTION | `FUNCTION MOD(X,2) NOT = 0` | `('_FUNC_MOD', '<>', '0')` |
### 6.2 复合条件 (parse_compound_condition)
构建 CondAnd/CondOr/CondNot/CondLeaf 树。优先级: AND > OR。
### 6.3 MC/DC (mcdc_sets)
为复合条件生成 Modified Condition/Decision Coverage 约束集。每个条件独立影响决策。
### 6.4 满足值 (satisfying_value)
数值: 边界值 (val-1, val, val+1)。字母数字: 字符级增减。Class: 匹配/不匹配类型值。
---
## 7. 路径枚举 (design.py)
### 7.1 核心算法 (enum_paths)
入口: `enum_paths(node, fields)` -> `list[(constraints, assignments)]`
| 节点类型 | 路径生成 |
|---------|---------|
| Assign | 单路径, 空约束 + 赋值 |
| BrSeq | 子路径笛卡尔积 + 去重 |
| BrIf | True 分支 + False 分支 两组路径 |
| BrEval | 每个 WHEN 一条路径 + OTHER (EVALUATE TRUE 用 MC/DC) |
| BrPerform | Enter + Skip 两条路径 |
| BrSearch | 每个 WHEN + AT END |
| CallNode | 黑盒: 单路径 |
**BrIf 简单条件:**
```
true_sub = enum_paths(true_seq)
false_sub = enum_paths(false_seq)
result = [(field, op, val, True) + sp for sp in true_sub]
+ [(field, op, val, False) + sp for sp in false_sub]
```
**BrIf 复合条件:**
```
sets = mcdc_sets(cond_tree, fields)
for constraints, decision in sets:
body = enum_paths(true_seq if decision else false_seq)
result.append(constraints + body)
```
### 7.2 路径截断 (_cap_paths)
最大 50,000 条路径 (`_MAX_PATHS`)。公平截断:
- Phase 1: 每个前置路径至少保留一条子路径
- Phase 2: 用剩余配额填充未覆盖分支
- 哨兵路径 (STOP/ABEND) 始终保留
### 7.3 EVALUATE TRUE 特殊处理 (eval_true_branch_constraints)
- 每个 WHEN 解析为复合条件
- MC/DC 集合为每个 WHEN 生成
- `prior_false` 累积所有前序 WHEN 的 false 集 (笛卡尔积)
- 每个 True 路径与所有可能的 prior-false 组合配对
---
## 8. 值生成 (design.py)
### 8.1 基础记录生成 (make_base_record)
入口: `make_base_record(seq_num, fields)` -> `dict`
1. 遍历所有字段:
- VALUE 子句 -> `_apply_value` 初始值
- 数值字段 -> `_make_numeric_value` 序列值
- 字母字段 -> `_make_alpha_value` 序列值
- 日期字段 -> `seq_date`
2. REDEFINES: 父字段值复制到重定义字段
3. 组 REDEFINES: 按位置递归复制子字段
4. 跨 FD 字段对齐: 同名不同前缀的数值字段共享 index
### 8.2 约束应用 (apply_constraint)
入口: `apply_constraint(rec, field, op, value, want_true, fields, ...)`
处理管线:
1. 变量下标解析: `WS-FIXED-VALUE(WS-IDX)` -> 具体下标
2. 下标传播: 裸字段名应用到所有下标变体
3. REDEFINES 重定向: 约束转到父字段 (共享存储)
4. 组字段展开: 组比较分解为子字段约束
5. Class 条件: `IS NUMERIC/ALPHABETIC` 通过 satisfying_value 处理
6. 字母比较: 字符级边界值
7. 数值比较: 整数边界值
8. 算术表达式: 启发式 steering
9. 零保护: 防止零值字段在 False 分支取最小值
10. 字段间协调: `WS-A >= WS-B` 同时设置两个字段
### 8.3 赋值传播 (propagate_assignments)
模拟程序数据流到每个决策点:
| Pass | 操作 |
|------|------|
| 1 | MOVE 传播 (源复制到目标) |
| 2 | COMPUTE 求值 (算术表达式) |
| 3 | ADD/SUBTRACT/MULTIPLY/DIVIDE |
| 3.5 | READ INTO (文件读到工作存储) |
| 4 | UNSTRING 分割 |
| 5 | INITIALIZE (填充零/空格) |
| 6 | STRING 拼接 |
| 7 | SET TO FALSE (88 级条件名) |
### 8.4 链追溯 (trace_to_root)
沿 MOVE/COMPUTE 链回溯到源字段。返回 `(root_field, chain_of_assignments)`。用于检测不可能路径 (字面量 MOVE 与约束矛盾)。
---
## 9. 覆盖率分析 (coverage.py)
### 9.1 决策点收集 (collect_decision_points)
| 节点 | 决策点 |
|------|-------|
| BrIf | kind="IF", branches=["T", "F"], label=条件 |
| BrEval | kind="EVALUATE", branches=["WHEN ...", "OTHER"] |
| BrPerform | kind="PERFORM", branches=["Enter", "Skip"] |
| BrSearch | kind="SEARCH", branches=["WHEN ...", "AT END"] |
每个决策点包含:
- `id`: 自增编号
- `cond_tree`: 复合条件树 (用于 evaluate_tree)
- `cond_leaves`: 叶条件列表 (用于 _match_leaf)
- `active_branches`: 已覆盖的分支集合
- `implied_branches`: 推断的覆盖分支
### 9.2 分支覆盖标记 (mark_coverage)
对每条路径的约束列表, 逐个决策点匹配:
**IF 标记** (`_mark_if`):
- 简单条件: 匹配 (field, op, value) 确定 T/F 分支
- 复合条件: 构建 leaf->bool assignment, 用 evaluate_tree 求值
- 合成函数 (_FUNC_*): 全部标记覆盖
**EVALUATE 标记** (`_mark_eval`):
- 简单: 匹配 subject 值确定 WHEN 分支
- EVALUATE TRUE: prior_false 累积 + 逐 WHEN 匹配
**PERFORM 标记** (`_mark_perform`):
- 条件为真 -> Enter, 条件为假 -> Skip
**叶条件标记** (`_match_leaf`):
- 去除下标后匹配 field + op + value
- covered_true / covered_false 独立追踪
### 9.3 HTML 报告生成
`generate_coverage_index()`: 中文 HTML 报告, 包含:
- 覆盖率统计 (总分支数 / 已覆盖 / 未覆盖)
- 每个决策点的覆盖状态 (badge: 覆盖/未覆盖)
- 叶条件级别的 MC/DC 覆盖详情
---
## 10. 输出生成 (output.py)
### 10.1 JSON 输出 (output_json)
入口: `output_json(records, outpath, roles, fd_fields, field_to_fd, open_dir, term_types, db_input, data_fields)`
**输出格式:**
```json
{
"program": "程序名",
"records": [
{
"input": { "R01EMP-ID": "A0000001", ... },
"expected_output": { "W01RESULT": "PASS", ... },
"working_storage": { "WS-COUNT": "003", ... },
"termination": "normal"
}
],
"db_input": { ... }
}
```
**字段分组逻辑:**
- `input`: 方向为 INPUT/I-O 的 FD, 角色为 input/inout 的字段
- `expected_output`: 方向为 OUTPUT/I-O 的 FD, 角色为 output/inout 的字段
- `working_storage`: 不属于任何 FD 的字段
- 未分配字段 (`_assigned_fields` 集合) 的归属判定
### 10.2 平面文件输出 (output_input_files)
将 JSON 记录写入 COBOL 输入文件 (固定长度/行顺序)。支持:
- DISPLAY 格式 (文本)
- COMP/COMP-3 格式 (二进制打包)
- RECORDING MODE V (变长记录, 4 字节 RDW 前缀)
---
## 11. 接口定义
### 11.1 公开 API (__init__.py)
```python
def extract_structure(source: str, ...) -> dict:
"""解析 COBOL 控制流 -> dict (字段定义、分支树、赋值)"""
def generate_data(source: str, ...) -> list[dict]:
"""生成测试数据 -> list[dict] (每条路径一条记录)"""
def incremental_supplement(...) -> list[dict]:
"""差分补充数据 -> list[dict]"""
```
### 11.2 核心模块 API
**read.py:**
```python
def preprocess(source: str, extra_search_paths: list[str] = None) -> str
def extract_data_division(source: str) -> str
def extract_procedure_division(source: str) -> str
def parse_data_division(dd_text: str) -> list[FieldDef]
def parse_file_section(source: str) -> dict
def parse_file_control(source: str) -> dict
def scan_open_statements(source: str) -> dict
def scan_all_file_directions(source: str) -> dict
def resolve_copybooks(source: str, base_dir: str, ...) -> str
def resolve_sql_includes(source: str) -> str
```
**core.py:**
```python
def build_branch_tree(proc_text: str, fields: list, full_source: str = None) -> (BrSeq, dict)
def scan_paragraphs(raw_lines: list, blocked_names: set = None) -> dict
def sql_register_virtual_fields(fields_dict: list[dict]) -> list[dict]
def classify_field_roles(fields, assignments, file_sec, ...) -> dict
```
**cond.py:**
```python
def parse_single_condition(text: str, fields: list = None) -> tuple | None
def parse_compound_condition(text: str, fields: list = None) -> CondAnd | CondOr | CondLeaf | CondNot | None
def collect_leaves(tree) -> list[CondLeaf]
def evaluate_tree(tree, assignment: dict) -> bool
def is_field(name: str, fields: list) -> bool
def mcdc_sets(tree, fields: list = None) -> list | None
def satisfying_value(pic_info: dict, op: str, value: str, want_true: bool) -> str
def merge_field_constraints(cons_list: list) -> list
```
**design.py:**
```python
def enum_paths(node, fields: list) -> list[(list, dict)]
def make_base_record(seq_num: int, fields: list) -> dict
def apply_constraint(rec: dict, field_name: str, op: str, value: str, want_true: bool, fields: list, ...) -> None
def propagate_assignments(rec: dict, assignments: dict, fields: list, ...) -> None
def trace_to_root(field: str, assignments: dict, fields: list, path_assign: dict) -> (str, list)
def generate_records(path_infos: list, data_fields: list, ...) -> (list, list, list)
def get_term_type(cons: list) -> (list, str)
def extend_abend_programs(names: list[str]) -> None
```
**coverage.py:**
```python
def collect_decision_points(node, fields: list) -> (list[DecisionPoint], list[LeafStat])
def mark_coverage(decision_points: list, leaf_stats: list, branch_paths: list, fields: list) -> None
def run_coverage(source: str, records: list, ...) -> dict
def generate_coverage_index(...) -> str # HTML string
```
**output.py:**
```python
def output_json(records: list, outpath: Path, ...) -> None
def output_input_files(records: list, outpath: Path, ...) -> None
```
**to_sql.py:**
```python
def collect_sql_meta(source: str, fields: list) -> list[dict]
def build_db_input(sql_meta: list, records: list, ...) -> dict
```
**flatfile.py:**
```python
def analyze_fd_layout(source_text: str, ...) -> dict[str, dict]
def write_flat_file(records: list, layout: dict, outpath: Path) -> None
```
---
## 12. 错误处理
### 12.1 解析错误 (ParseError)
`models.py` 定义 `ParseError` 数据类:
- `line: int` -- 错误所在行号
- `message: str` -- 错误描述
- `severity: str` -- 'warning' 或 'error'
`ProcParseResult.errors` 收集解析过程中产生的所有错误。
### 12.2 处理策略
| 错误类型 | 处理方式 |
|---------|---------|
| COPYBOOK 找不到 | 跳过该 COPY, 继续解析, 记录 warning |
| Lark 语法不匹配 | 回退到规则引擎, 标记 `fallback_to_ai=True` |
| 未知 COBOL 语句 | 跳过该行, 不中断解析 |
| 字段未找到 | 使用默认值, 记录 debug 日志 |
| 条件解析失败 | 返回 None, 由上层决定回退策略 |
| 路径枚举超过上限 | `_cap_paths` 公平截断到 50,000 条 |
| 新解析器超时/失败 | `pipeline_bridge` 回退到旧解析器 (3s 超时) |
| 旧解析器超时/失败 | 返回空 BrSeq + 空 assignments |
| gcov 执行失败 | 返回空 dict, 跳过覆盖率标记 |
| 外部调用异常 | try/except 捕获, logger.warning 记录, 不中断主流程 |
### 12.3 日志策略
所有模块使用 Python `logging`:
- `logger.info()` -- 关键流程节点 (解析开始/结束, 路径数, 记录数)
- `logger.debug()` -- 详细调试信息 (约束应用, 赋值传播)
- `logger.warning()` -- 非致命错误 (解析失败, 回退)
- `logger.error()` -- 致命错误 (不应发生)
### 12.4 已知限制
1. **MC/DC 复合 IF bug**: `merge_field_constraints` 合并同字段约束会破坏 `_match_leaf` 匹配
2. **OCCURS DEPENDING ON**: 捕获但未用于记录生成
3. **88 级 VALUE**: 目标程序无 88 级 VALUE 子句, 解析器不依赖此特性
4. **路径多样性丢失**: 规则引擎 100 路径限制 (LLM 模式 50000 路径不受影响)
5. **合成函数字段**: `_FUNC_MOD` 等合成名 is_field 返回 False, 叶条件不可匹配
+574
View File
@@ -0,0 +1,574 @@
# DB 管道编排器 - 详细设计文档
> 模块路径: `orchestrator_db.py`
> 版本: V3 (2026技术大赛)
> 行数: 1973
> 核心类: `GixsqlOrchestrator`
---
## 1. 模块概述
### 1.1 职责
`orchestrator_db.py` 是 DB 型 COBOL 程序的 6 步端到端测试管道编排器,负责:
1. **环境整备** - gixpp 预处理 + cobc 编译(Step 1
2. **输入数据生成** - 测试数据生成 + 平面文件输出 + DB 初始化(Step 2)
3. **COBOL 执行** - 调用编译后的 COBOL 程序(Step 3
4. **中间数据提取** - SQLite DB → Java 中介 JSONStep 4
5. **Java 执行** - 调用 Java 转换程序(Step 5
6. **结果验证** - Java 输出与 COBOL 期望值比较(Step 6
### 1.2 边界
| 在范围内 | 不在范围内 |
|---------|-----------|
| DB 型 COBOL 程序 6 步管道编排 | Flat-file 型 COBOL 程序处理 |
| SQLite 数据库初始化与种子注入 | 分支树构建(由 cobol_testgen/ 处理) |
| 多场景(多轮)执行调度 | 覆盖率报告生成(可选附件,非管道核心) |
| 测试数据合成与 PK 冲突注入 | Java 程序内部逻辑 |
### 1.3 依赖关系
```
orchestrator_db.py
+-- config.py <- 项目配置
+-- config/program_schema.py <- YAML 程序定义
+-- cobol_testgen/ <- 核心引擎
+-- data/diff_result.py <- 验证结果数据模型
+-- runners/gixsql_runner.py <- GnuCOBOL 编译-执行运行器
+-- agents/llm.py <- LLM 客户端(可选)
```
---
## 2. 核心数据结构
### 2.1 GixsqlOrchestrator
```python
class GixsqlOrchestrator:
def __init__(self, config: Config, program_id: str,
cobol_src_dir: str | Path,
copybook_dirs: list[str | Path] | None = None,
work_dir: str | Path | None = None,
skip_jvm: bool = True):
```
**关键属性:**
| 属性 | 类型 | 说明 |
|------|------|------|
| config | Config | 项目配置 |
| program_id | str | COBOL 程序标识符 |
| cobol_src_dir | Path | COBOL 源码目录 |
| copybook_dirs | list[Path] | COPYBOOK 搜索路径 |
| work_dir | Path | 构建产物目录(ASCII 路径) |
| runtime_dir | Path | 运行时数据目录 |
| schema | ProgramSchema | YAML 程序定义 |
| runner | GixsqlCobolRunner | GnuCOBOL 运行器 |
| db_path | Path | 默认 SQLite DB 路径 |
| skip_jvm | bool | 是否跳过 Step 5/6 |
**管道状态:**
| 状态属性 | 类型 | 说明 |
|----------|------|------|
| src_path | Path | 预处理后源码路径 |
| pp_path | Path | gixpp 预处理输出路径 |
| exe_path | Path | 编译后可执行文件路径 |
| java_input_path | Path | Java 中介数据路径 |
| java_output_path | Path | Java 输出路径 |
| _current_db_path | Path | 当前场景的 DB 路径 |
| _multi_run_gcov_data | dict | 多轮合并后的 gcov 数据 |
| generated_records | list[dict] | 生成的测试数据记录 |
| generated_structure | dict | 解析后的程序结构 |
### 2.2 DbPipelineResult
```python
@dataclass
class DbPipelineResult:
program_id: str
step: int | float
success: bool
message: str = ""
data: dict = field(default_factory=dict)
```
---
## 3. 6 步流程设计
### 3.1 Step 1: 环境整备 (step1_setup_environment)
**职责:** gixpp 预处理 → cobc 编译
**执行流程:**
```
1. _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/)
2. runner.preprocess(src, preprocessed/, copybook_dirs)
|-- gixpp 预处理 + CONNECT TO 路径修补
| gixpp 错误转换: 'data/kin.db' → 'sqlite://localhost/kin'
| 修补为: 'sqlite:///{db_path}'
3. runner.compile(pp, exe, copybook_dirs, extra_srcs)
|-- cobc 编译 → work_dir/bin/{program_id}.exe
|-- 编译日志写入 runtime_dir/logs/compile/
4. 返回 DbPipelineResult(step=1, success, data={exe_path, log})
```
**关键逻辑:**
- 源码必须复制到 ASCII-only 路径(gixpp 不支持中文路径)
- CONNECT TO 字符串修补: gixpp 输出的 `sqlite://localhost/kin` 需替换为绝对路径
- 子程序从多个候选目录搜索,未找到仅 warning 不阻断
**输入:** cobol_src_dir, copybook_dirs, schema.subprograms
**输出:** src_path, pp_path, exe_path
### 3.2 Step 2: 输入数据生成 (step2_generate_inputs)
**职责:** COBOL 解析 → 测试数据生成 → DB 初始化 → 平面文件输出
**执行流程:**
```
1. COBOL 解析
|-- extract_structure(src_text) → 分支树 + 赋值表
|-- generate_all_data() → 测试数据记录(白盒+机能+策略)
|-- 后处理: R02APPL-ID 链接 R01APPL-ID
2. DB 初始化
|-- 确定 DB 路径(场景分离: {program_id}_{scenario_id}.db
|-- 清理旧 DB → _init_database(db_path)
| |-- _create_tables(): 按 YAML schema 创建表 + 主键
|-- _populate_database(): 注入种子行
| |-- 解析 COBOL → 分支树 → 路径枚举
| |-- build_db_input(): 生成 DB 输入行
| |-- 覆盖率驱动数据补充(日期、假期等)
| |-- 区间协调(INSURANCE-RATES / EMP-MASTER
| |-- INSERT OR IGNORE 写入 DB
|-- _inject_extra_seed_rows(): 大结果集注入
|-- _inject_sql_error_rows(): PK 冲突行注入
3. 记录修补
|-- records[0].R01EMP-ID = SPACE(触发空社员路径)
|-- 全零 EMP-ID → SPACE 清洗
|-- R01LINE 与 EMP-ID 一致性修补
|-- 注入重复 EMP-IDAGG UPDATE 路径)
|-- _deduplicate_r01_pk(): PK 去重
|-- _inject_aggregation_boundaries(): 聚合边界数据
4. 场景驱动修改
|-- collision 场景: INSERT 重复、OVT-MONTHLY 匹配、COMMIT 阈值
|-- abnormal 场景: orphan cancel ABEND
5. 平面文件输出
|-- write_all_files(): 全 FD 平面文件
|-- write_sysin_file(): SYSIN 配置
|-- _seed_matching_monthly_rows(): MONTHLY_ABSENCE 匹配行预填
6. JSON 输出(可选)
|-- 解析 DATA DIVISION → 字段字典
|-- 分支树 + MC/DC 路径枚举
|-- output_json(): 写入 json/{program_id}.json
7. 返回 DbPipelineResult(step=2, data={records, flat_files, db_path})
```
**关键逻辑:**
- 多场景时 DB 路径分离: `{program_id}_{scenario_id}.db`
- PK 冲突行必须与运行时 INSERT 实际值一致(基于输入记录而非合成值)
- 聚合边界注入: overflow(同月累加溢出)+ table-full>=110 个不同月)
- 日期值统一为 YYYYMMDD 格式
**输入:** src_path, pp_path, schema, scenario
**输出:** generated_records, generated_structure, db_path, 平面文件
### 3.3 Step 3: COBOL 执行 (step3_run_cobol)
**职责:** 调用编译后的 COBOL 程序并收集 gcov 覆盖率数据
**执行流程:**
```
1. 环境准备
|-- 创建 runtime/run_{id}/main/{input,output}/ 目录
|-- 复制生成的平面文件 → input/
|-- 复制 JSON → json/
2. 文件方向映射 (_scan_assign_to)
|-- 正则扫描 SELECT/ASSIGN-TO → {文件名: 方向}
|-- OPEN 语句解析 → INPUT/OUTPUT 方向确定
3. DB 路径准备
|-- 场景 DB → 复制到默认 DB 路径
|-- CWD/data/kin.dbCONNECT TO 路径)
|-- CWD/kingixsql regex 路径)
4. 执行
|-- 清理前次 .gcda 文件
|-- runner.run(exe, cwd, db_path, env_overrides, command_args)
|-- 日志写入 runtime_dir/logs/
5. gcov 数据收集
|-- .gcda 从 CWD + exe_dir 复制到 gcov/run_{id}/
|-- .gcno 同步(共享 .gcnoCOPY 不 MOVE
6. 返回 DbPipelineResult(step=3, data={returncode, log, ...})
```
**关键逻辑:**
- GIXSQL_DB_PATH 环境变量不生效,需通过 CWD/data/kin.db 传递
- GnuCOBOL 的 .gcda 写入编译时 CWD,多场景需 COPY 到各自 gcov 目录
- subprogram 的 .gcno 必须同步到每个 run 目录
**输入:** exe_path, schema, scenario
**输出:** 运行日志、gcov 数据、返回码
### 3.4 Step 4: 中间数据提取 (step4_extract_intermediate)
**职责:** 从 SQLite DB 导出 Java 程序所需的 JSON 中介数据
**执行流程:**
```
1. 打开 DB_current_db_path 或 db_path
2. 遍历 schema.db_tables,对每张表执行 SELECT * FROM [table]
3. 构建 meta = {program_id, tables: {table_name: [rows]}}
4. 写入 work_dir/intermediate/{program_id}_W01.json
5. 返回 DbPipelineResult(step=4, data={tables, w01_path})
```
**关键逻辑:**
- 使用 sql_name 或 name 查询表名
- 即使表不存在也不报错(空列表),允许部分执行
- 输出 JSON 包含所有表的全量行数据
**输入:** _current_db_path, schema.db_tables
**输出:** java_input_pathW01 JSON
### 3.5 Step 5: Java 执行 (step5_run_java)
**职责:** 调用 Java 转换程序处理 COBOL 输出数据
**执行流程:**
```
1. 创建 java_output 目录
2. 构建命令: java -jar {java_jar} -i {java_input_path} -o {java_out}
3. subprocess.run(cmd, capture_output=True, timeout=60)
4. 返回 DbPipelineResult(step=5, data={returncode, log})
```
**关键逻辑:**
- 超时限制 60 秒
- 若未指定 java_jar,仅执行 java -version 检测环境
- 依赖 Step 4 的输出作为输入
**输入:** java_input_path, java_jar
**输出:** java_output_path, 执行日志
### 3.6 Step 6: 结果验证 (step6_verify)
**职责:** 比较 Java 输出与 COBOL 期望值
**执行流程:**
```
1. 构建 VerificationRun 结果对象
2. 读取 DB 各表行数(调试信息)
3. 扫描 java_output_path 下的 .txt/.json 文件
4. 设置 exit_code 和 statusPASS/MISMATCH
5. 返回 VerificationRun
```
**关键逻辑:**
- fields_mismatched == 0 时判定为 PASS
- 输出 Java 输出文件列表作为调试信息
- 返回 VerificationRun 而非 DbPipelineResult
**输入:** java_output_path, _current_db_path, schema.db_tables
**输出:** VerificationRunstatus, exit_code, debug
---
## 4. 接口定义
### 4.1 主入口: run_all()
```python
def run_all(self, skip_steps: set[int] | None = None,
generate_coverage: bool = True) -> VerificationRun:
```
**参数:**
| 参数 | 类型 | 说明 |
|------|------|------|
| skip_steps | set[int] | 要跳过的步骤编号集合(如 {5, 6}) |
| generate_coverage | bool | 是否生成覆盖率报告(默认 True) |
**返回:** VerificationRun(最终验证结果)
**行为:**
- skip_jvm=True 时自动将 {5, 6} 加入 skip_steps
- 多场景执行: schema.runs 非空时循环执行 Step 2-3
- 每个场景失败即返回 BLOCKED(不继续后续步骤)
- Step 1 只执行一次(编译共享)
- 多场景执行后自动合并 gcov 数据
### 4.2 单步接口
| 方法 | 签名 | 返回 |
|------|------|------|
| step1_setup_environment | () -> DbPipelineResult | 编译结果 |
| step2_generate_inputs | (scenario: ScenarioDef?) -> DbPipelineResult | 数据生成结果 |
| step3_run_cobol | (scenario: ScenarioDef?) -> DbPipelineResult | 执行结果 |
| step4_extract_intermediate | () -> DbPipelineResult | 提取结果 |
| step5_run_java | (java_cmd, java_jar) -> DbPipelineResult | Java 执行结果 |
| step6_verify | () -> VerificationRun | 验证结果 |
| generate_coverage_report | (output_dir?) -> DbPipelineResult | 覆盖率报告 |
### 4.3 内部辅助接口
| 方法 | 职责 |
|------|------|
| _copy_sources_to_workdir | 源码 + COPYBOOK + 子程序复制到 ASCII 工作目录 |
| _scan_assign_to | 扫描 SELECT/ASSIGN-TO + OPEN 确定文件方向 |
| _init_database / _create_tables | 按 YAML schema 创建 SQLite 表结构 |
| _populate_database | 从测试记录生成 DB 种子行 |
| _inject_sql_error_rows | 注入 PK 冲突行触发 SQL 错误路径 |
| _inject_extra_seed_rows | 为 SELECT 型程序注入大结果集 |
| _inject_aggregation_boundaries | 注入聚合边界数据(溢出 + 表满) |
| _deduplicate_r01_pk | 确保 R01 记录 PK 唯一性 |
| _seed_matching_monthly_rows | 预填 MONTHLY_ABSENCE 匹配行 |
| _merge_multi_run_gcov | 多轮场景 gcov 数据合并 |
| _merge_schema_columns | YAML schema 列型合并到 declared_columns |
| _insert_pk_map | 构建 SQL 表 → PK 列名映射 |
| _coordinate_db_rule_matching | DB 属性区间对齐(AGE/DEPENDENTS/REGION |
| _coordinate_seed_numeric_types | DB 种子值数字化(PIC 9 对齐) |
| _make_synthetic_error_rows | 构建合成 PK 冲突行 |
---
## 5. 数据流
### 5.1 管道级数据流
```
cobol_src_dir/{program_id}.cbl
|
v
[Step 1: 环境整备]
|-- src_path (预处理源码)
|-- pp_path (gixpp 输出)
|-- exe_path (编译产物)
|
v
[Step 2: 输入数据生成]
|-- generated_records (测试数据)
|-- generated_structure (分支树 + 赋值表)
|-- db_path (SQLite DB with seeds)
|-- 平面文件 (input/)
|-- JSON (json/{program_id}.json)
|
v
[Step 3: COBOL 执行]
|-- 运行日志
|-- 输出文件 (output/)
|-- gcov 数据 (gcov/)
|
v
[Step 4: 中间数据提取]
|-- java_input_path (W01 JSON)
|
v
[Step 5: Java 执行]
|-- java_output_path
|
v
[Step 6: 结果验证]
|-- VerificationRun (PASS/MISMATCH)
```
### 5.2 每步输入输出明细
| 步骤 | 输入 | 输出 | 依赖 |
|------|------|------|------|
| Step 1 | cobol_src_dir, copybook_dirs, schema | src_path, pp_path, exe_path | 无 |
| Step 2 | src_path, pp_path, schema, scenario | records, structure, db_path, flat files | Step 1 |
| Step 3 | exe_path, records, db_path, scenario | logs, output files, gcov data | Step 1, 2 |
| Step 4 | db_path, schema.db_tables | java_input_path | Step 2, 3 |
| Step 5 | java_input_path, java_jar | java_output_path | Step 4 |
| Step 6 | java_output_path, db_path | VerificationRun | Step 4, 5 |
### 5.3 多场景数据流
```
schema.runs = [scenario_A, scenario_B, ...]
|
v
[Step 1] 编译一次(共享 exe_path
|
v
[Step 2-A] scenario_A → db_A, records_A, flat_A
[Step 3-A] 运行 A → gcov_A
|
v
[Step 2-B] scenario_B → db_B, records_B, flat_B
[Step 3-B] 运行 B → gcov_B
|
v
[_merge_multi_run_gcov] gcov_A + gcov_B → merged_gcov
|
v
[Step 4] 提取最后一个场景的 DB
[Step 5-6] Java 执行 + 验证
```
---
## 6. 错误处理
### 6.1 步骤级容错
每个 Step 方法内部用 try-except 包裹,返回 DbPipelineResult(success=False) 而非抛出异常:
```python
def step1_setup_environment(self) -> DbPipelineResult:
try:
# ... 编译逻辑 ...
return DbPipelineResult(self.program_id, 1, result.success, ...)
except Exception as e:
return DbPipelineResult(self.program_id, 1, False, str(e))
```
### 6.2 管道级中断
run_all() 中每个 Step 后检查 success,失败则立即返回 BLOCKED:
```python
r1 = self.step1_setup_environment()
if not r1.success:
return VerificationRun(status="BLOCKED", step_reached=1)
r2 = self.step2_generate_inputs(scenario)
if not r2.success:
return VerificationRun(status="BLOCKED", step_reached=2)
```
### 6.3 异常分类
| 异常场景 | 处理方式 | 影响 |
|----------|----------|------|
| gixpp 预处理失败 | Step 1 返回 success=False | 管道终止 |
| cobc 编译失败 | Step 1 返回 success=False | 管道终止 |
| COBOL 运行崩溃 | Step 3 返回 success=False | 管道终止 |
| DB 表不存在 | OperationalError 捕获,空列表 | 不阻断 |
| Java 超时 | subprocess.TimeoutExpired | Step 5 返回 False |
| gcov 文件缺失 | PermissionError 捕获,跳过 | 不阻断 |
| COPYBOOK 未找到 | logger.warning | 不阻断 |
| 子程序未找到 | logger.warning | 不阻断 |
| JSON 输出失败 | logger.warning | 不阻断,继续执行 |
### 6.4 数据一致性保障
- **PK 去重:** `_deduplicate_r01_pk` 确保所有 R01 记录的 (EMP_ID, DATE) 唯一
- **EMP-ID 清洗:** 全零 '00000000' → SPACE,避免 PK 冲突导致 ABEND
- **日期格式统一:** 所有日期值统一为 YYYYMMDD 8 位格式
- **DB 列型匹配:** INSERT 前按 PRAGMA table_info 转换值类型(INTEGER/DECIMAL
---
## 7. 性能设计
### 7.1 编译复用
Step 1 只执行一次,所有场景共享编译产物(exe_path)。
### 7.2 多场景顺序执行
Step 2-3 对每个场景顺序执行,避免 DB 并发写入冲突。每个场景有独立的:
- DB 文件: `{program_id}_{scenario_id}.db`
- 工作目录: `work_dir/run_{scenario_id}/`
- 运行目录: `runtime_dir/run_{scenario_id}/`
### 7.3 gcov 数据合并
多场景执行后调用 `_merge_multi_run_gcov()`,对每行取 max(count) 合并:
```python
merged[line] = max(merged.get(line, 0), cnt)
```
子程序 gcov 单独存储(`_sub_gcov_data`),避免行号冲突。
### 7.4 文件复制策略
- 构建产物放在 TEMP 目录(ASCII 路径),避免 gixpp 中文路径问题
- 运行时数据放在项目 runtime/ 目录
- DB 文件在多场景间通过 shutil.copy2 复制,而非共享
- .gcda/.gcno 使用 COPY 而非 MOVEGnuCOBOL 累积写入特性)
### 7.5 已执行步骤跳过
run_all() 支持 skip_steps 参数,允许跳过已执行的步骤:
```python
orch.run_all(skip_steps={1, 2, 3}) # 仅执行 Step 4-6
```
### 7.6 覆盖率报告可选
覆盖率报告生成由 generate_coverage 控制,默认开启但非管道核心路径:
```python
if '--coverage' in cv_flags and generate_coverage:
self.generate_coverage_report()
```
### 7.7 LLM 可选
Step 2 的 LLM 客户端仅在 config.llm_model 配置时初始化,未配置时回退到规则引擎:
```python
llm = None
if hasattr(self.config, 'llm_model') and self.config.llm_model:
llm = LLMClient(model=self.config.llm_model, timeout=self.config.llm_timeout)
recs = generate_all_data(..., llm_client=llm, ...)
```
---
## 8. 目录结构
```
runtime/{program_id}/
+-- main/
| +-- input/ <- 平面输入文件
| +-- output/ <- COBOL 输出文件
| +-- json/ <- JSON 输出
+-- logs/
| +-- compile/ <- 编译日志
| +-- {program_id}.log <- 运行日志
+-- gcov/
| +-- run_{scenario}/ <- 各场景 gcov 数据
+-- run_{scenario}/ <- 多场景隔离目录
+-- main/input/
+-- main/output/
work_dir/{program_id}/
+-- src/ <- ASCII 源码副本
+-- preprocessed/ <- gixpp 输出
+-- bin/ <- 编译产物 (.exe, .gcno)
+-- main/
| +-- input/ <- 生成的平面文件
| +-- json/ <- JSON 输出
+-- intermediate/ <- W01 JSONJava 中介数据)
+-- java_output/ <- Java 输出
+-- run_{scenario}/ <- 多场景隔离目录
```
+412
View File
@@ -0,0 +1,412 @@
# 03 - runners 编译运行引擎
## 1. 模块概述
`runners` 模块负责 COBOL 程序与 Java 程序的**编译、运行、覆盖率采集**全流程。模块采用策略模式,通过抽象基类 `Runner` 统一不同语言运行时的接口,对外暴露一致的 `compile -> run -> get_coverage` 三阶段管线。
核心职责:
| 职责 | 说明 |
|------|------|
| COBOL 编译 | 调用 `cobc`COBOL 编译器)将 `.cbl` 源码编译为可执行文件 |
| Java 编译 | 调用 `mvn package` 将 Maven 项目打包为 `.jar` |
| 运行执行 | 通过 `subprocess` 启动编译产物,捕获 stdout/stderr 和返回码 |
| 测试数据写入 | 将 `TestCase` 列表序列化为 COBOL 二进制 / JSON 格式 |
| 覆盖率报告 | 采集分支覆盖数据(gcov / JaCoCo)并返回量化报告 |
---
## 2. 文件清单
```
runners/
__init__.py # 包导出:公开 API 声明
runner.py # 抽象基类 Runner + 数据类 BuildResult / RunResult / CoverageReport
cobol_runner.py # COBOL 编译·执行器(cobc 管线)
gixsql_runner.py # DB COBOL 程序编译·执行器(gixpp + cobc + gixsql 链接)
native_java_runner.py # Java 本地运行器(mvn + java -jar
spark_java_runner.py # Spark 运行器(spark-submit
data_writer.py # 测试数据序列化(COBOL 二进制 / Spark JSON / Native JSON
```
---
## 3. 接口定义
### 3.1 数据类(runner.py
```python
@dataclass
class BuildResult:
success: bool # 编译是否成功
artifact_path: str = "" # 编译产物路径(.exe / .jar
log: str = "" # 编译日志(stdout + stderr
@dataclass
class RunResult:
success: bool # 运行是否成功(returncode == 0
records: list[dict] # 运行输出记录(JSON 格式)
log: str = "" # 运行日志
coverage_exec: str = "" # 覆盖率执行文件路径
@dataclass
class CoverageReport:
branch_rate: float = 0.0 # 分支覆盖率(0.0 ~ 1.0
covered_branches: int = 0 # 已覆盖分支数
total_branches: int = 0 # 总分支数
verdict: str = "PASS" # 判定结果(PASS / FAIL
```
### 3.2 抽象基类(runner.py
```python
class Runner(ABC):
@abstractmethod
def compile(self, source_dir: str) -> BuildResult: ...
@abstractmethod
def run(self, artifact: str, input_path: str, output_path: str) -> RunResult: ...
@abstractmethod
def get_coverage(self, artifact: str, run_id: str) -> CoverageReport: ...
```
### 3.3 CobolRunnercobol_runner.py
| 方法 | 签名 | 说明 |
|------|------|------|
| `compile` | `(src, dialect="ibm", gcov=False) -> BuildResult` | 旧式编译,`-std=ibm-strict`,供 orchestrator.py 使用 |
| `run` | `(binary, input_path, output_path) -> RunResult` | 旧式执行,stdin 管道 stdout |
| `compile_with_links` | `(src, work_dir, copybook_dirs, sub_objects, gcov) -> BuildResult` | 新式编译:主程序 + 链接 SUB.o,支持 COPYBOOK 搜索路径和 gcov |
| `run_file_based` | `(binary, run_dir, input_files, timeout) -> RunResult` | 新式执行:基于文件的 I/O,将输入文件复制到运行目录后启动程序 |
### 3.4 GixsqlCobolRunnergixsql_runner.py
| 方法 | 签名 | 说明 |
|------|------|------|
| `preprocess` | `(src_path, out_dir, copybook_dirs) -> str` | gixpp 预处理:COPY 展开、SQL 归一化、格式修正 |
| `compile` | `(pp_path, exe_path, copybook_dirs, extra_srcs) -> GixsqlBuildResult` | cobc 编译,链接 gixsql 库 |
| `run` | `(exe_path, work_dir, db_path, ...) -> GixsqlRunResult` | 执行 DB 程序,设置 SQLite 数据库路径 |
| `read_db_tables` | `(db_path, table_names) -> list[GixsqlTableData]` | 读取 SQLite 数据库表内容 |
### 3.5 NativeJavaRunnernative_java_runner.py
| 方法 | 签名 | 说明 |
|------|------|------|
| `compile` | `(source_dir) -> BuildResult` | Maven 打包,输出 target/program.jar |
| `run` | `(artifact, input_path, output_path) -> RunResult` | java -jar 执行,解析 stdout JSON 行 |
| `get_coverage` | `(artifact, run_id) -> CoverageReport` | 检查 jacoco.exec 是否存在,返回覆盖率 |
### 3.6 SparkJavaRunnerspark_java_runner.py
| 方法 | 签名 | 说明 |
|------|------|------|
| `compile` | `(source_dir) -> BuildResult` | Maven 打包,输出 target/program.jar |
| `run` | `(artifact, input_path, output_path) -> RunResult` | spark-submit 执行,读取 part-\* 输出文件 |
| `get_coverage` | `(artifact, run_id) -> CoverageReport` | 返回固定 0.80 覆盖率(Spark 无原生覆盖率集成) |
### 3.7 DataWriterdata_writer.py
| 方法 | 签名 | 说明 |
|------|------|------|
| `write_cobol_binary` | `(cases, out)` | 将 TestCase 列表写为 COBOL 二进制格式(大端序 int64 / float64 / ASCII |
| `write_spark_json` | `(cases, cfg, d)` | 写 Spark 输入 JSONpart-00000.json),key 字段加序号后缀 |
| `write_native_json` | `(cases, out)` | 写 Native JSON(每行一个 JSON 对象) |
---
## 4. 编译流程
### 4.1 COBOL 编译(cobol_runner.py
#### 旧式编译流程
```
.cbl 源文件
|
v
cobc -x -std=ibm-strict [-o 输出路径] [-g] [--coverage] 源文件
|
v
BuildResult(success, artifact_path, log)
```
- 默认超时 30 秒
- `gcov=True` 时追加 `--coverage` 参数生成 `.gcno` 文件
#### 新式编译流程(带链接)
```
.cbl 源文件 + SUB*.o 对象文件
|
v
cobc -x -g [--coverage] [-I COPYBOOK路径...] -o 输出.exe 源文件 SUB1.o SUB2.o ...
|
v
BuildResult(success, exe_path, log)
```
- 默认超时 120 秒
- 工作目录切换至 `work_dir`(确保 `.gcno` 产出位置正确)
- 不使用 `-std=ibm-strict`,依赖默认方言
### 4.2 DB COBOL 编译(gixsql_runner.py
DB 程序编译采用 gixpp + cobc 两阶段管线:
```
原始 .cbl 源文件
|
v -- _normalize_source()
| 1. 剥离注释行(避免日文注释中的 EXEC SQL 被误匹配)
| 2. EXEC SQL INCLUDE SQLCA -> COPY SQLCA
| 3. EXEC SQL CONNECT TO 'literal' -> CONNECT TO :WS-GIX-CONN USER :WS-GIX-USR
| 4. COPY REPLACING 内联展开(Python 侧)
| 5. ALL COPY 展开(替代 cobc -E
| 6. 关键字间多空格压缩
| 7. DIVISION/SECTION 头部列位置修正(Area A,列 8 起)
| 8. SELECT ... FROM ... INTO -> SELECT ... INTO ... FROMgixpp 要求 INTO 在前)
| 9. CURRENT TIMESTAMP -> CURRENT_TIMESTAMPSQLite 后端兼容)
| 10. DB2 schema 限定表名去限定(SCHEMA.TABLE -> TABLE
v
_norm.cbl(规范源文件)
|
v -- preprocess()
| gixpp -i _norm.cbl -o _pp.cbl -e [-I COPYBOOK路径...]
| 将 EXEC SQL 块转换为 GIXSQL 调用序列
v
_pp.cbl(预处理后源文件)
|
v -- compile()
| 1. _patch_sql_identifiers()DB2 连字符标识符 -> 下划线(SQLite 兼容)
| 2. _patch_sqlcode_normalize()SQLite 约束错误码映射为 DB2 标准码
| 3. cobc -x -L gixsql库路径 -K GIXSQL*函数 -l gixsql [-I COPYBOOK...] -o exe pp.cbl
v
GixsqlBuildResult(success, exe_path, log)
```
### 4.3 Java 编译(native_java_runner.py / spark_java_runner.py
两个 Java Runner 共享相同的 Maven 编译流程:
```
source_dir/pom.xml
|
v
mvn -B package -f pom.xml
|
v
source_dir/target/program.jar
|
v
BuildResult(success, artifact_path, log)
```
- 默认超时 120 秒
- 使用 `-B`batch mode)避免交互式提示
- 产物固定为 `target/program.jar`
---
## 5. 运行流程
### 5.1 COBOL 执行
#### 旧式执行(stdin 到 stdout
```
input_path(二进制数据)
|
v subprocess.run([binary], input=data, capture_output=True)
|
v stdout 写入 output_path
|
v RunResult(success)
```
- 超时 30 秒
- 无输出记录解析(仅写入文件)
#### 新式执行(基于文件)
```
input_files: {assign_name: source_path}
|
v 复制输入文件到 run_dir
|
v subprocess.run([binary], cwd=run_dir, capture_output=True)
|
v RunResult(success, log)
```
- 超时 60 秒(可配置)
- 工作目录为 `run_dir`,程序通过 ASSIGN 名读取文件
- 日志截断至 2000 字符
### 5.2 DB COBOL 执行(gixsql_runner.py
```
exe_path + db_pathSQLite 数据库)
|
v 部署 DLL 到 exe_dirlibgixsql.dll, libgixsql-sqlite.dll 等)
|
v 复制输入文件到 work_dir
|
v subprocess.run([exe], cwd=work_dir, env={GIXSQL_DB_PATH: db_path})
|
v GixsqlRunResult(success, returncode, db_path, log)
|
v read_db_tables(db_path, table_names) -> 读取数据库输出
```
- 超时 30 秒(可配置)
- 环境变量 `GIXSQL_DB_PATH` 指向 SQLite 数据库
- DLL 部署策略:优先 lib_path,回退到 gixpp bin 目录
- returncode 0 或 1 均视为成功(COBOL STOP RUN 返回码差异)
### 5.3 Java 执行
#### NativeJavaRunner
```
input_pathJSON 文件)
|
v java -jar artifactstdin 输入)
|
v 解析 stdout JSON 行 -> records 列表
|
v RunResult(success, records, log)
```
- 超时 60 秒
- 每行一个 JSON 对象
#### SparkJavaRunner
```
input_pathJSON 文件)
|
v spark-submit --class Main --master local[*]
| --conf spark.input.path=...
| --conf spark.output.path=...
| --conf spark.input.format=json
| --conf spark.output.format=json
| artifact
|
v 读取 output_path/part-* 文件 -> records 列表
|
v RunResult(success, records, log)
```
- 超时 300 秒(5 分钟)
- 输入输出格式通过 spark conf 配置
- 输出文件自动 glob 匹配 `part-*`
---
## 6. 错误处理
### 6.1 编译失败
| 场景 | 处理方式 | 返回值 |
|------|----------|--------|
| cobc 编译错误 | 捕获 returncode != 0 | `BuildResult(success=False, log=stdout+stderr)` |
| cobc 超时 | 捕获 TimeoutExpired | `BuildResult(success=False, log="Compile timeout")` |
| gixpp 预处理失败 | 捕获 returncode != 0,抛出 RuntimeError | `raise RuntimeError("gixpp failed")` |
| Maven 编译错误 | 捕获 returncode != 0 | `BuildResult(success=False, log=stdout+stderr)` |
### 6.2 运行时异常
| 场景 | 处理方式 | 返回值 |
|------|----------|--------|
| COBOL 程序异常终止 | 捕获 returncode != 0 | `RunResult(success=False, log=stdout+stderr)` |
| COBOL 程序超时 | 捕获 TimeoutExpired | `RunResult(success=False, log="Run timeout")` |
| DB 程序 returncode 1 | 视为成功(COBOL 语义差异) | `GixsqlRunResult(success=True)` |
| Java 程序异常 | 捕获 returncode != 0 | `RunResult(success=False, log=stdout+stderr)` |
| Spark 程序超时 | 捕获 TimeoutExpired300s | `RunResult(success=False, log="Run timeout")` |
| DLL 未找到 | 运行时加载失败,日志记录 | `GixsqlRunResult(success=False, log=...)` |
### 6.3 Gixsql 专用处理
| 机制 | 说明 |
|------|------|
| SQL 标识符归一化 | DB2 连字符标识符自动转换为下划线(`EMP-MASTER` -> `EMP_MASTER` |
| SQLCODE 映射 | SQLite 约束错误码(-1555/-2067/-19)映射为 DB2 标准码(-803 |
| Schema 限定去限定 | `SCHEMA.TABLE` -> `TABLE`SQLite 无 schema 概念) |
| CURRENT TIMESTAMP | DB2 `CURRENT TIMESTAMP` -> SQLite `CURRENT_TIMESTAMP` |
| DLL 自动部署 | 运行前将 gixsql DLL 复制到 exe_dir 确保加载器找到 |
### 6.4 日志截断
所有 Runner 的日志均截断至固定长度以避免内存溢出:
| Runner | 日志截断长度 |
|--------|-------------|
| CobolRunner(旧式) | 无截断(完整 stdout+stderr |
| CobolRunner(新式) | 2000 字符 |
| GixsqlCobolRunner | 500-1000 字符 |
| NativeJavaRunner | 无截断(完整 stdout+stderr |
| SparkJavaRunner | 无截断(完整 stdout+stderr |
---
## 7. 设计特点
### 7.1 双轨架构
CobolRunner 维护两套接口:
- **旧式接口**`compile` + `run`):供 `orchestrator.py` 使用,兼容现有调用链
- **新式接口**`compile_with_links` + `run_file_based`):支持非 DB 程序的文件 I/O 和 SUB.o 链接
两套接口互不干扰,通过不同方法名区分。
### 7.2 Gixsql 预处理管线
GixsqlCobolRunner 的预处理是模块中最复杂的部分:
1. **Python 侧 COPY 展开**:替代 `cobc -E`,解决 gixpp ESQL 解析器对 COPY REPLACING 伪文本的兼容问题
2. **SQL 归一化**:处理 DB2 与 SQLite 的语法差异(标识符、时间函数、schema 限定符)
3. **SQLCODE 映射**:注入代码将 SQLite 特定错误码转换为 DB2 标准码,确保 `IF SQLCODE = -803` 分支可达
### 7.3 DLL 部署策略
gixsql 运行时需要多个 DLLlibgixsql.dll, libgixsql-sqlite.dll 等)。部署策略:
1. 优先从 `lib_path` 复制
2. 若不存在,回退到 `gixpp` bin 目录
3. 检查文件大小避免重复复制
### 7.4 DataWriter 格式
| 格式 | 字节序 | 数值类型 | 字符串处理 |
|------|--------|----------|------------|
| COBOL 二进制 | 大端序(Network Byte Order | int64 (`>q`) / float64 (`>d`) | ASCII,右补空格至 10 字节 |
| Spark JSON | N/A | JSON 数字 | UTF-8key 字段加序号后缀 |
| Native JSON | N/A | JSON 数字 | UTF-8,每行一个 JSON 对象 |
---
## 8. 依赖关系
```
runners/
runner.py -> 无外部依赖
cobol_runner.py -> runners.runner, subprocess, pathlib
gixsql_runner.py -> runners.runner(仅类型引用),subprocess, sqlite3, pathlib, logging
native_java_runner.py -> runners.runner, subprocess, json, shutil, pathlib
spark_java_runner.py -> runners.runner, subprocess, json, shutil, pathlib
data_writer.py -> data.test_case.TestCase, struct, json, pathlib
```
外部工具依赖:
| 工具 | 用途 | 使用者 |
|------|------|--------|
| `cobc` | COBOL 编译器 | CobolRunner, GixsqlCobolRunner |
| `gixpp` | COBOL EXEC SQL 预处理器 | GixsqlCobolRunner |
| `mvn` | Java 构建工具 | NativeJavaRunner, SparkJavaRunner |
| `java` | Java 运行时 | NativeJavaRunner |
| `spark-submit` | Spark 提交工具 | SparkJavaRunner |
| `libgixsql.dll` | gixsql 运行时库 | GixsqlCobolRunner(运行时链接) |
| `libgixsql-sqlite.dll` | SQLite 后端驱动 | GixsqlCobolRunner(运行时加载) |
@@ -0,0 +1,416 @@
# HINA 程序分类模块 - 详细设计文档
> 模块路径: `hina/`
> 版本: V3 (2026技术大赛)
---
## 1. 模块概述
### 1.1 职责
`hina` 模块是 COBOL 迁移验证平台 V3 的程序分类与质量门禁系统,负责:
1. **程序分类** - 根据 COBOL 源码特征,将程序归类到预定义类型
2. **确信度评估** - 多因子计算分类结果的可信度
3. **质量门禁** - 测试数据生成前检查覆盖率和边界条件
4. **策略匹配** - 根据分类结果选择测试策略模板
5. **gcov 覆盖率收集** - 编译运行后采集动态代码覆盖率
6. **分层重试** - 处理编译/运行错误的自愈和重试机制
### 1.2 依赖关系
```
hina/
__init__.py <- 公开 API 入口
classifier.py <- L1 关键字规则匹配 + 结构性匹配检测
confidence.py <- 4 因子确信度计算
gate.py <- 质量门禁检查
gcov_collector.py <- gcov 覆盖率采集
hina_agent.py <- LLM 混淆组分类代理
retry.py <- 分层重试处理器
strategy.py <- 策略模板 + 必须项补充
pipeline/pipeline.py <- 完整分类管道
rule_engine/confusion_groups.py <- 8 个混淆对解析函数
rule_engine/contradiction.py <- 矛盾检测与解决
rule_engine/backtrack.py <- 多轮回溯判定
```
---
## 2. 文件清单
| 文件 | 行数 | 职责 |
|------|------|------|
| `__init__.py` | 25 | 公开 API 入口,导出 classify_program |
| `classifier.py` | 304 | L1 关键字规则(14条)、注释剥离、KEY比较检测、结构性匹配检测 |
| `confidence.py` | 120 | 4 因子确信度计算 |
| `gate.py` | 106 | 质量门禁检查、双模式质量评分 |
| `gcov_collector.py` | 58 | gcov 覆盖率采集 |
| `hina_agent.py` | 283 | LLM 混淆组分类、规则兜底 |
| `retry.py` | 82 | 分层重试: 自愈修复 + 朴素重试 |
| `strategy.py` | 103 | 策略模板(5个类型) + 补充 |
| `pipeline/pipeline.py` | 698 | 完整分类管道: 3条路径 |
| `rule_engine/confusion_groups.py` | 287 | 8个混淆对解析函数 |
| `rule_engine/contradiction.py` | 163 | 矛盾检测与解决 |
| `rule_engine/backtrack.py` | 96 | 多轮回溯判定 |
---
## 3. 分类算法
### 3.1 分类管道总览
`classify_program()` 流程:
```
COBOL 源码
-> 并行: detect_keyword() + extract_structure()
-> 根据最高关键字确信度选择路径:
>= 90% -> 路径 A: keyword 直接输出
50-89% -> 路径 B: 规则引擎 + 确信度计算
< 50% -> 路径 C: LLM 辅助 + 规则验证
-> 匹配子类型区分(仅对匹配/键中断程序)
-> 输出最终 JSON
```
### 3.2 L1 关键字规则 (classifier.py)
定义在 `L1_RULES` 中,共 14 条规则,格式 `(分类名称, [关键字列表], 置信度阈值)`:
| 分类 | 关键字 | 置信度 |
|------|--------|--------|
| DB操作 | EXEC SQL | 0.95 |
| 子程序调用 | CALL, LINKAGE SECTION | 0.90 |
| IS INITIAL | IS INITIAL | 0.99 |
| SYSIN | ACCEPT ... FROM SYSIN | 0.90 |
| 编码转换 | ALPHABETIC, ASCII, EBCDIC | 0.85 |
| online | DFHCOMMAREA | 0.95 |
| SORT | SORT ... ON ... KEY | 0.95 |
| MERGE | MERGE ... ON ... KEY | 0.95 |
| 替代索引 | ALTERNATE RECORD KEY | 0.99 |
| 编辑输出 | WRITE ... AFTER/BEFORE | 0.80 |
| 文件编成 | ORGANIZATION IS | 0.99 |
| マッチング | WS-[*]KEY* 变量模式(3条) | 0.55-0.65 |
`re:` 前缀表示正则表达式匹配,无前缀表示字面量包含匹配。
### 3.3 结构性匹配检测
`_detect_matching_structure` 不依赖 KEY 变量名,通过 6 个信号判断匹配程序:
| 信号 | 检测内容 |
|------|---------|
| 1 | READ ... AT END |
| 1b | 2+ 个 READ 语句 |
| 2 | PERFORM UNTIL ... = 'Y'/'N' |
| 3 | ELSE ... READ (条件性读取) |
| 4 | IF A = B (跨文件字段比较) |
| 5 | 2+ 个 OPEN INPUT |
信号 >= 5: 0.55, = 4: 0.50, = 3: 0.40, < 3: 0.0
### 3.4 KEY 变量比较检测
`_matches_key_comparison` 确认 KEY 变量在比较上下文中实际使用:
- 模式 1: WS-KEY = / > / < (排除 Figurative Constant)
- 模式 2: 非 WS- 前缀 KEY
- 模式 3: READ INTO ... KEY
---
## 4. 确信度计算 (confidence.py)
### 4.1 4 因子公式
```
confidence = base x context_factor x consistency_factor x structure_factor
```
### 4.2 因子定义
**上下文因子:**
| match_count | 值 |
|-------------|-----|
| >= 3 | 1.0 |
| 2 | 0.95 |
| 1 | 0.90 |
| 0 | 0.50 |
| 共识奖励 | +0.15 (上限 1.0) |
**一致性因子:**
| 矛盾情况 | 值 |
|----------|-----|
| 无矛盾 | 1.0 |
| 全部已解决 | 0.90 |
| 未解决 < 3个 | 0.80 |
| 未解决 >= 3个 | 0.50 |
**结构一致性因子:**
| score | 值 |
|-------|-----|
| 5 | 1.0 |
| >= 3 | 0.7 |
| >= 1 | 0.5 |
| 0 | 0.3 |
### 4.3 判定结果
| 确信度范围 | 判定 | 需人工审核 |
|-----------|------|-----------|
| >= 0.90 | auto | 否 |
| 0.70-0.89 | review | 是 |
| 0.50-0.69 | manual | 是 |
| < 0.50 | impossible | 是 |
---
## 5. 规则引擎 (rule_engine/)
### 5.1 混淆组判定 (confusion_groups.py)
8 个混淆对解析函数:
| 混淆对 | 区分逻辑 | 置信度 |
|--------|---------|--------|
| matching_vs_keybreak | 三路IF+多文件->マッチング; WS-PREV-KEY+累加器->キーブレイク | 0.75-0.90 |
| dedup_vs_nodedup | WS-PREV-KEY存在->含重复; 不存在->不含重复 | 0.50-0.90 |
| validation_vs_keybreak | WS-ERR*字段->校验; WS-*CNT计数器->キーブレイク | 0.55-0.85 |
| csv_merge_vs_split | STRING+逗号->合并; INSPECT REPLACING+逗号->拆分 | 0.85 |
| simple_vs_two_stage | OPEN-CLOSE-再OPEN->二段階; 其他->単純 | 0.50-0.90 |
| pure_vs_mixed | has_switch+has_counter+IF>=3->混合 | 0.70 |
| division_50_25_100 | DIVIDE被除数常量匹配 | 0.95 |
| mn_output_mode | SELECT>=3+分支>=3->M:N | 0.55-0.65 |
### 5.2 特征注入 (pipeline.py)
从 COBOL 源码注入额外特征:
| 特征名 | 检测方式 | 用途 |
|--------|---------|------|
| has_key_var | 正则匹配KEY变量比较 | 防止计数器比较误触发 |
| has_structural_match | IF+跨文件字段比较+循环/读取 | 结构性匹配信号 |
| has_cross_file_cmp | IF A = B | 跨文件比较 |
| has_csv_merge | STRING ... ',' ... INTO | CSV合并信号 |
| has_csv_split | INSPECT ... REPLACING ... ',' | CSV拆分信号 |
### 5.3 矛盾检测与解决 (contradiction.py)
**矛盾对定义** (CONTRADICTION_PAIRS): 10 对可能冲突的分类类型。
**解决策略**:
1. 优先级比较 (TYPE_PRIORITY): マッチング(10) > キーブレイク(9) > 項目チェック(8) > ...
2. 优先级相同时,调用混淆对解析器重判定 (置信度 >= 0.80 则采纳)
3. 最终回退: 取 type_a
### 5.4 多轮回溯 (backtrack.py)
`BacktrackResolver` 封装多轮判定:
- 最大轮次: 3
- 超时: 30 秒
- 超时/超轮次: 标记 `backtrack_degraded = True`,降级返回
---
## 6. LLM 辅助分类 (hina_agent.py)
### 6.1 混淆组分类 Prompt
`CONFUSION_PROMPT` 包含 7 个混淆组定义:
1. simple_sequential - 极少决策点
2. condition_heavy - IF语句占比高
3. evaluate_driven - EVALUATE主导
4. data_file_centric - 文件操作密集
5. search_intensive - SEARCH ALL
6. call_based - CALL语句
7. mixed_complex - 多种复杂特征
### 6.2 规则兜底分类
LLM 失败时 `_fallback_classification` 基于结构特征优先级:
| 优先级 | 条件 | 分类 |
|--------|------|------|
| 1 | total_decisions == 0 | simple_sequential |
| 2 | has_search_all | search_intensive |
| 3 | has_call | call_based |
| 4 | evaluate > if 且 >= 2 | evaluate_driven |
| 5 | file_count >= 2 | data_file_centric |
| 6 | if >= 5 或 decisions >= 8 | condition_heavy/nested_if |
| 7 | if >= 2 | condition_heavy/simple_if |
复杂度升级: >= 3 个复杂度标志 -> mixed_complex
---
## 7. 匹配子类型区分
仅对 マッチング/キーブレイク/項目チェック 执行子类型区分。
### 7.1 分层策略
**第 1 层 - 静态规则:**
| 条件 | 子类型 |
|------|--------|
| 二段階 in category | 二段階 |
| file_count >= 3 + WS-SAVE-KEY | M:N->MxN |
| WS-PREV-KEY | 混合 |
| WS-MAST-KEY + WS-TRAN-KEY | 1:N |
| WS-KEY-M + WS-KEY-T | N:1 |
| WS-KEY-M + WS-KEY-N | M:N |
**第 2 层 - LLM 推理**: 多键变量+多文件时调用 LLM 判断子类型
**第 3 层 - 回退**: 多键+多文件->M:N; 对称键名->1:1
---
## 8. 策略模板 (strategy.py)
### 8.1 策略模板定义
| 分类 | 必须项 | 边界项 |
|------|--------|--------|
| マッチング | COM-N001~A003, MT-N001~N006 | MT-B001, MT-B002 |
| キーブレイク | COM-N001, A002, KB-N001~N005, A001 | KB-B001, KB-B002 |
| 条件分岐 | B-N001, N003, N006, N009 | - |
| 内部表検索 | T-N001, N002, A001, A002 | - |
| 項目チェック | VF-N001, N002, N004, A001 | - |
### 8.2 补充函数
- `supplement()`: 从模板追加全部必须项和边界项
- `supplement_only()`: 增量补充指定必须项
---
## 9. 质量门禁 (gate.py)
### 9.1 门禁检查
| 检查项 | 条件 | 输出 |
|--------|------|------|
| 决策点覆盖率 | branch_rate < 0.90 | decision_gaps |
| 段落覆盖率 | paragraph_rate < 1.0 | paragraph_gaps |
| 测试数据为空 | not complete_tests | no_data |
### 9.2 双模式质量评分
```
gcov 未启用: branch_rate*0.5 + paragraph_rate*0.5 + confidence*0.4
gcov 启用: static_cov*0.3 + gcov_cov*0.4 + confidence*0.3
```
---
## 10. gcov 覆盖率收集 (gcov_collector.py)
### 10.1 采集流程
1. 检查 .gcda 文件是否存在
2. 执行 gcov 命令 (30s 超时)
3. 查找 .gcov 输出文件
4. 解析行覆盖率
### 10.2 降级策略
所有 gcov 失败降级为仅静态分析。
---
## 11. 分层重试 (retry.py)
### 11.1 重试机制
`RetryHandler(max_heal=2, max_simple=3)`:
- PASS/QUALITY_WARN -> 返回结果
- BLOCKED/ERROR -> 自愈修复或朴素重试
- 总次数 >= 5 -> 标记 FATAL
### 11.2 自愈修复
| 错误 | 检测 | 修复 |
|------|------|------|
| compile_error | 日志含 "not found" | 设置 COB_LIBRARY_PATH |
| s0c7 | 日志含 "S0C7" | 记录警告 |
---
## 12. 接口定义
### 12.1 公开 API
```python
def classify_program(cobol_source: str, llm=None) -> dict:
"""返回: {category, confidence, needs_review, method, source,
judgment, matches, contradictions, v2_confidence, structure}"""
```
### 12.2 内部模块 API
- `classifier.detect_keyword(source)` -> list[tuple[str, float, str]]
- `confidence.compute_confidence_v2(keyword_result, structure_features, ...)` -> dict
- `gate.check(complete_tests, hina_result, coverage, ...)` -> dict
- `gate.compute_quality_score(static_coverage, gcov_coverage, confidence)` -> float
- `strategy.get_strategy(hina_type)` -> dict
- `strategy.supplement(base_tests, hina_result)` -> list[dict]
- `gcov_collector.collect_gcov(cobol_src, work_dir)` -> dict
- `RetryHandler(max_heal, max_simple).run(pipeline_fn)` -> VerificationRun
- `hina_agent.classify_with_llm(structure, llm)` -> dict
- `rule_engine.resolve_confusion_pair(features, pair_name)` -> dict
- `rule_engine.detect_contradictions(features)` -> list[dict]
- `rule_engine.resolve_contradiction(features, contradiction)` -> str
- `BacktrackResolver(structure_extractor).resolve(source, features)` -> dict
---
## 13. 错误处理
### 13.1 错误分类
| 类别 | 示例 | 处理 |
|------|------|------|
| L1解析错误 | 正则匹配失败 | 跳过该规则 |
| LLM调用失败 | API超时/网络错误 | 回退到规则引擎 |
| LLM响应解析 | 无效JSON | 返回unknown |
| gcov失败 | 命令未找到/超时 | 降级为静态分析 |
| 回溯超时 | > 30s | 标记降级返回 |
| 矛盾不可解 | 优先级相同 | 取type_a回退 |
| 空源码 | cobol_source为空 | 返回impossible |
### 13.2 日志策略
- `logger.info()`: 关键流程节点
- `logger.debug()`: 规则引擎特征、矛盾详情
- `logger.warning()`: LLM失败、回溯降级、gcov失败
---
## 14. 数据流总结
```
Input: cobol_source (str), llm (optional)
|
v
[Parallel] detect_keyword + extract_structure
|
v
Route by max keyword confidence:
>= 0.90 -> Path A: keyword direct
0.50-0.89 -> Path B: rule engine
< 0.50 -> Path C: LLM assisted / rule fallback
|
v
_resolve_matching_subtype (for matching/keybreak programs)
|
v
Output: {category, confidence, needs_review, method, source,
judgment, matches, contradictions, v2_confidence, structure}
```
下游集成:
1. orchestrator_db.py: 根据category选择测试策略
2. strategy.py: 获取必须项
3. gate.py: 用confidence计算质量评分
4. data_merger.py: 合并策略数据到测试记录
+319
View File
@@ -0,0 +1,319 @@
# 05 - LLM 代理模块详细设计
## 1. 模块概述
LLM 代理模块(`agents/`)是 COBOL 迁移验证平台 V3 的智能分析层,负责通过大语言模型(LLM)完成三项核心任务:
1. **COPYBOOK 解析**Agent1):将 COBOL COPYBOOK 源码解析为结构化字段树(`FieldTree`
2. **测试数据设计**(Agent2):基于字段树生成边界测试用例(`TestSuite`
3. **差异诊断**Agent3):对 COBOL/Java 字段比对不一致项进行根因分析与建议
此外,模块还包含一个**式样书驱动测试数据生成器**(`DesignDataGenerator`),通过解析日文详细设计书,结合 LLM 生成有业务意义的机能测试数据。
模块底层封装了统一的 `LLMClient`,提供缓存、重试、多模型兼容能力。
## 2. 文件清单
| 文件 | 职责 | 依赖 |
|------|------|------|
| `__init__.py` | 包入口,公开 API 导出 | 所有子模块 |
| `llm.py` | LLM API 客户端(缓存 + 重试) | `httpx` |
| `agent1_parser.py` | COPYBOOK → FieldTree 解析代理 | `llm.py`, `data.field_tree` |
| `agent2_data.py` | FieldTree → TestSuite 测试数据设计代理 | `llm.py`, `data.field_tree`, `data.test_case` |
| `agent3_diagnostic.py` | FieldResult → 诊断建议文本代理 | `llm.py`, `data.diff_result` |
| `design_data.py` | 式样书驱动测试数据生成器 | `llm.py`, `design_data_input_parser` |
| `design_data_input_parser.py` | 式样书 .md 解析器 | 标准库 `re`, `dataclasses` |
## 3. Agent1 解析代理(Agent1Parser
### 3.1 职责
将 COBOL COPYBOOK 源码文本发送给 LLM,由 LLM 输出结构化 JSON,再转换为 `FieldTree` 对象。这是整个流水线的第一步——只有正确解析字段结构,后续测试生成和比对才有基础。
### 3.2 数据流
```
COPYBOOK 源码文本
↓ (LLM call)
JSON: {"fields": [{name, level, pic, usage, offset, length, decimal, signed, occurs, redefines, conditions, children}]}
↓ (_load / _fields 递归)
FieldTree
```
### 3.3 核心类
```python
class Agent1Parser:
def __init__(self, llm: LLMClient)
def parse(self, text: str) -> FieldTree
def _load(self, d: dict) -> FieldTree
def _fields(self, raw: list[dict], off: int) -> list[Field]
```
### 3.4 LLM 提示词
系统提示词(P1)要求 LLM 扮演 COBOL COPYBOOK 解析器角色,输出严格 JSON 格式,包含字段的名称、层级、PIC 子句、USAGE 类型、偏移量、长度、小数位、符号、OCCURS、REDEFINES、88-level 条件及子字段嵌套。
### 3.5 字段偏移量计算
`_fields` 方法递归遍历 JSON 字段列表,逐字段计算字节偏移量(`cur += f.length`),确保每个字段在记录中的物理位置准确。子字段通过递归调用 `_fields` 处理嵌套层级。
### 3.6 错误处理
- LLM 返回非法 JSON 时,`parse` 捕获异常,返回一个 `FieldTree(copybook_name="parse_error")` 空树
- 使用 bare `except` 捕获所有解析异常,保证流水线不中断
## 4. Agent2 数据生成代理(Agent2Data
### 4.1 职责
基于 `FieldTree` 结构,通过 LLM 生成边界测试用例集合(`TestSuite`)。每个测试用例指定字段值和覆盖目标决策点。
### 4.2 数据流
```
FieldTree
↓ (flatten → JSON)
{"fields": [{name, pic, usage, length, decimal, signed}]}
↓ (LLM call)
{"test_cases": [{id, fields: {FIELD: value}, coverage_targets: [DP-001]}]}
↓ (构造 TestCase)
TestSuite
```
### 4.3 核心类
```python
class Agent2Data:
def __init__(self, llm: LLMClient)
def design(self, tree: FieldTree, target="boundary", spark_mode=False) -> TestSuite
```
### 4.4 LLM 提示词
系统提示词(P2)要求 LLM 扮演 COBOL 测试数据设计师角色,根据字段树生成边界测试用例,输出严格 JSON 格式。
### 4.5 Spark 模式
`spark_mode=True` 时,生成的 `TestSuite` 附带 `SparkConfig(num_records=1000)`,用于大数据量测试场景。
### 4.6 错误处理
- LLM 调用失败或返回非法 JSON 时,生成一条兜底用例 `TestCase(id="TC-FALLBACK", fields={"BR-AMT": 0})`
- 使用 bare `except` 捕获反序列化异常
## 5. Agent3 诊断代理(Agent3Diagnostic
### 5.1 职责
对单个字段比对结果(`FieldResult`)进行根因分析,输出包含问题类型、置信度、原因和建议的 JSON 诊断文本。
### 5.2 数据流
```
FieldResult (field_name, cobol_value, java_value, status)
↓ (格式化 prompt)
LLM call
JSON: {"issue_type": "...", "confidence": 0.5, "reason": "...", "suggestion": "..."}
```
### 5.3 核心类
```python
class Agent3Diagnostic:
def __init__(self, llm: LLMClient)
def analyze(self, fr: FieldResult) -> str
```
### 5.4 LLM 提示词
系统提示词(P3)明确要求 LLM **不做 PASS/FAIL 判定**,仅提供诊断分析。这是设计上的重要约束——Agent3 只是辅助分析工具,最终判定由比对引擎完成。
### 5.5 错误处理
- 依赖 `LLMClient.call()` 的重试机制
- 返回原始 LLM 响应字符串,调用方负责解析
## 6. 式样书驱动测试数据生成器(DesignDataGenerator
### 6.1 职责
从日文详细设计书(.md)中提取程序元信息(`ProgramMeta`),结合 COBOL 源码、COPYBOOK 结构和 DB 定义,通过 LLM 生成有业务意义的机能测试数据。
### 6.2 数据流
```
设计书 .md + COBOL 源码
↓ (DesignDataInputParser.parse)
ProgramMeta (program_id, pgm_pattern, files, keys, process_detail, ...)
↓ (加载规则 + 构建 prompt)
LLM call
JSON: {"records": [{field_name: value}]}
↓ (_resolve_field_names 字段名映射)
list[dict]
```
### 6.3 核心类
```python
class DesignDataGenerator:
def __init__(self, llm_client: LLMClient, cpy_dirs: list, rules_dir: str = "rules")
def generate(self, design_md_text, source_text, file_db_md_text=None,
db_md_text=None, replacing_rules=None, v3_field_names=None) -> list[dict]
```
### 6.4 式样书解析器(DesignDataInputParser
解析日文详细设计书 Markdown 文档,提取以下结构化信息:
| 数据类 | 内容 |
|--------|------|
| `ProgramMeta` | 程序ID、程序名、系统名、PGM类型、PGM模式、功能概要 |
| `FileInfo` | 文件编号、文件/DB名、标识符、DD名、I/O类型、COPY群、记录格式、记录长度、媒体 |
| `KeyInfo` | 键文件名、排序条件、键条件 |
| `ModuleInfo` | 模块编号、功能、程序ID、COPY名 |
| `TableInfo` | 表名、DB ID、列定义、主键列 |
解析通过 Markdown 标题匹配(`## 基本情報``## 使用ファイル一覧` 等)和表格解析实现,不依赖外部 Markdown 解析库。
### 6.5 输入类型自动判定
`_determine_input_type` 根据输入文件的媒体类型自动判定:
| 媒体类型 | input_type |
|----------|------------|
| 仅 PS(顺序文件) | `file` |
| 仅 DB(数据库) | `db` |
| 混合或无输入 | `mixed` / `file` |
### 6.6 字段名映射(_resolve_field_names
外部 Agent 生成的字段名可能与 V3 内部字段名不一致,映射规则:
1. **REPLACING 展开**`(A)``R01`
2. **前缀连字符处理**`R01-EMP-ID``R01EMP-ID`
3. **直接匹配**:精确匹配 V3 字段名
4. **去连字符匹配**`R01EMP-ID``R01EMPID`
5. **去下划线匹配**`R01_EMP_ID``R01EMPID`
6. **无法映射的字段丢弃**
### 6.7 规则加载
`_load_rules``rules/pgm_pattern/``rules/special_feature/` 目录加载所有 `.md` 规则文件,作为 LLM 上下文的一部分。
### 6.8 记录去重(_dedup
支持按指定键字段去重,`additional_records` 优先保留。
## 7. LLM 接口封装(LLMClient
### 7.1 职责
封装 LLM API 调用,提供文件缓存、自动重试、多模型兼容能力。所有 Agent 通过此客户端与 LLM 交互。
### 7.2 核心类
```python
class LLMClient:
def __init__(self, model="gpt-4o-mini", timeout=15, cache_dir=".cache/llm")
def call(self, messages: list[dict], retries=1) -> str
def _key(self, msgs: list[dict]) -> str # SHA256 哈希键
def _get(self, k: str) -> str | None # 缓存读取
def _set(self, k: str, v: str) -> None # 缓存写入
```
### 7.3 缓存机制
- **键生成**:对消息列表进行 JSON 序列化后取 SHA256 哈希
- **存储**:以 `{hash}.json` 文件存储在 `.cache/llm/` 目录
- **格式**`{"response": "..."}`
- **效果**:相同输入直接返回缓存结果,避免重复调用 LLM
### 7.4 重试机制
- 默认重试 1 次(共 2 次尝试)
- 使用 `httpx.post` 发送 HTTP 请求
- 重试时捕获所有异常,仅在最后一次失败时抛出
### 7.5 环境变量配置
| 环境变量 | 说明 | 默认值 |
|----------|------|--------|
| `LLM_API_KEY` | API 密钥 | 空字符串 |
| `OPENAI_API_KEY` | 备用 API 密钥 | 空字符串 |
| `LLM_API_BASE` | API 基础 URL | `https://api.openai.com/v1` |
### 7.6 API 调用格式
```json
POST {base}/chat/completions
{
"model": "{model}",
"messages": [...]
}
Header: Authorization: Bearer {key}
```
响应解析路径:`response["choices"][0]["message"]["content"]`
## 8. 接口定义
### 8.1 模块公开 API
```python
# agents/__init__.py
LLMClient # LLM API 客户端(含缓存 + 重试)
Agent1Parser # COPYBOOK → FieldTree
DesignDataGenerator # 式样书 → 机能测试数据
Agent2Data # FieldTree → TestSuite(测试数据设计)
Agent3Diagnostic # FieldResult → 诊断建议文本
```
### 8.2 典型调用链
```
Agent1Parser.parse(copybook_text) → FieldTree
Agent2Data.design(field_tree) → TestSuite
DesignDataGenerator.generate(design_md, source_text) → list[dict]
Agent3Diagnostic.analyze(field_result) → str (JSON)
```
### 8.3 数据模型依赖
| 模型 | 定义位置 | 用途 |
|------|----------|------|
| `FieldTree` | `data.field_tree` | 字段树结构 |
| `Field` | `data.field_tree` | 单个字段定义 |
| `TestCase` | `data.test_case` | 单条测试用例 |
| `TestSuite` | `data.test_case` | 测试用例集合 |
| `SparkConfig` | `data.test_case` | Spark 生成配置 |
| `FieldResult` | `data.diff_result` | 字段比对结果 |
## 9. 错误处理
### 9.1 错误策略
模块采用**宽容降级**策略:
| 异常场景 | 处理方式 | 影响 |
|----------|----------|------|
| LLM 返回非法 JSON | 返回空/兜底结果 | 流水线继续 |
| LLM API 调用失败 | 重试后抛出异常 | 上层捕获 |
| 式样书解析失败 | 返回空列表 | 跳过数据生成 |
| 字段名映射失败 | 丢弃无法映射的字段 | 部分数据丢失 |
| 缓存文件损坏 | 跳过缓存,重新调用 | 无功能影响 |
### 9.2 日志策略
- 使用 Python `logging` 模块
- 关键步骤记录 INFO 级别日志(解析开始、LLM 响应、记录数)
- 异常记录 WARNING 级别日志
- 字段映射丢弃记录 DEBUG 级别日志
### 9.3 已知局限
1. Agent1/Agent2/Agent3 的 bare `except` 可能掩盖非预期异常
2. 缓存键基于消息内容哈希,模型变更不会自动失效旧缓存
3. `DesignDataGenerator` 的 LLM prompt 包含截断(`process_detail[:2000]`),超长设计书可能丢失信息
+358
View File
@@ -0,0 +1,358 @@
# 06 - 比对模块详细设计
## 1. 模块概述
比对模块(`comparator/`)是 COBOL 迁移验证平台 V3 的核心验证引擎,负责将 COBOL 原始输出与 Java(Spark)迁移后输出进行逐字段对比,判断迁移正确性。
模块提供五项核心能力:
1. **记录对齐**aligner):按主键将 COBOL 记录集与 Java 记录集配对
2. **二进制读取**cobol_binary_reader):解析 COBOL 二进制输出文件为字典
3. **数据标准化**normalizer):处理 EBCDIC 编码、COMP-3 压缩十进制、日期格式
4. **字段比对**field_compare):按类型(数值/日期/字符串)进行字段级比较
5. **舍入检测**rounding_detect):判断数值差异是否由 COBOL ROUNDED 子句引起
## 2. 文件清单
| 文件 | 职责 | 依赖 |
|------|------|------|
| `__init__.py` | 包入口,公开 API 导出 | 所有子模块 |
| `aligner.py` | COBOL 与 Java 记录对齐 | 无 |
| `cobol_binary_reader.py` | 二进制 COBOL 输出解析 | `data.field_tree` |
| `field_compare.py` | 字段级比较(decimal/string/date | `data.diff_result` |
| `normalizer.py` | COMP-3/EBCDIC 解码、IR 记录构造 | 无 |
| `rounding_detect.py` | 舍入差异检测 | `decimal` |
## 3. 记录对齐算法(aligner.py
### 3.1 职责
将 COBOL 输出记录集和 Java 输出记录集按主键字段进行配对,输出对齐的记录对列表及匹配状态。
### 3.2 函数签名
```python
def align_records(
cobol_records: list[dict],
java_records: list[dict],
key_field: str = "CUST-ID"
) -> list[tuple]
```
### 3.3 算法描述
1. **分组**:分别按 `key_field` 对 COBOL 和 Java 记录建立 `{key: [records]}` 索引
2. **合并键集**:取两侧键的并集,按字符串排序
3. **逐键配对**:对每个键,取两侧记录列表的对应位置进行配对
4. **状态标记**
| 状态 | 含义 |
|------|------|
| `MATCHED` | 两侧均有记录,成功配对 |
| `MISSING_IN_SPARK` | COBOL 有记录,Java 侧缺失 |
| `EXTRA_IN_SPARK` | Java 有记录,COBOL 侧缺失 |
### 3.4 返回值
```python
list[tuple[dict | None, dict | None, str]]
# (cobol_record, java_record, status)
```
### 3.5 设计要点
- 使用 `setdefault` 聚合同键多记录,支持一对多场景
- 排序保证输出顺序稳定
- 键值转为字符串处理,兼容数值键和字符串键
- 空记录集直接返回空列表
## 4. 二进制读取器(cobol_binary_reader.py
### 4.1 职责
读取 COBOL 程序生成的二进制输出文件,根据 `FieldTree` 定义的字段布局逐记录解析为字典。
### 4.2 核心类
```python
class CobolBinaryReader:
def read(self, path: str, tree: FieldTree) -> list[dict]
def _record_size(self, tree: FieldTree) -> int
def _parse(self, record_bytes: bytes, tree: FieldTree) -> dict
def _comp3(self, raw: bytes, signed: bool, decimal: int) -> str
```
### 4.3 记录大小计算
```python
def _record_size(self, tree):
return max((f.offset + f.length for f in tree.fields), default=0)
```
取所有顶层字段的最大 `offset + length` 作为记录大小。
### 4.4 字段解析策略
| USAGE 类型 | 解析方式 |
|-----------|----------|
| `COMP-3` | 半字节解码 + 符号位处理 + 小数点定位 |
| `COMP` / `COMP-5` | `int.from_bytes(raw, "big", signed=...)` |
| 其他(DISPLAY 等) | ASCII 解码 + 去尾空格 |
### 4.5 COMP-3 解码算法
```
输入: N 字节压缩十进制数据
1. 将每字节拆为两个半字节(高4位 + 低4位)
2. 弹出最后一个半字节作为符号位
3. 逐半字节累加为数值(权值 = 10^(len-1-i)
4. 符号位 0xD/0xB -> 取反
5. 按 decimal 定位小数点
输出: 字符串形式数值(如 "1234.56"
```
### 4.6 错误处理
- 文件为空或记录大小为 0 -> 返回空列表
- 记录不完整(字节数 < record_size-> 跳过该记录
- COMP-3 空数据 -> 返回 "0"
- 非 ASCII 字符 -> `errors="replace"` 替换为 `?`
## 5. 数据标准化器(normalizer.py
### 5.1 职责
提供 COBOL 数据到中间表示(IR)的标准化转换,包括 EBCDIC 解码、COMP-3 解码、日期格式统一、IR 记录构造。
### 5.2 核心类
```python
class Normalizer:
def normalize_encoding(self, raw: bytes, encoding: str) -> str
def normalize_comp3(self, raw: bytes) -> str
def normalize_date(self, s: str) -> str
def to_ir_record(self, name, hex_, val, enc, ft, length=0, scale=0, signed=False) -> IRRecord
def to_null_ir(self, name: str, side: str = "java") -> IRRecord
```
### 5.3 EBCDIC 编码转换
内置 EBCDIC 037 代码页映射表(`EBCDIC_037`),覆盖:
- 空格、标点、运算符
- 小写 a-z0x81-0xA9
- 大写 A-Z0xC1-0xE9
- 数字 0-90xF0-0xF9
不可映射字符处理:
- 可打印 ASCII32-126-> 保留原字符
- 其他 -> 替换为 `?`
### 5.4 COMP-3 标准化
`CobolBinaryReader._comp3` 算法一致,但返回整数字符串(无小数点处理),适用于无需小数定位的场景。
### 5.5 日期标准化
- 8 位纯数字字符串(如 `20260822`-> 格式化为 `YYYY-MM-DD`
- 其他格式 -> 原样返回
### 5.6 中间表示(IR)模型
```python
@dataclass
class CobolIRField:
raw_hex: str # 原始十六进制
decoded_value: str # 解码后值
encoding: str # 编码类型(EBCDIC/ASCII
field_type: str # 字段类型
length: int # 字节长度
scale: int # 小数位数
signed: bool # 是否带符号
@dataclass
class JavaIRField:
raw_value: str # 原始值
decoded_value: str # 解码后值
field_type: str # 字段类型
nullable: bool # 是否可空
@dataclass
class IRRecord:
field_name: str
cobol: CobolIRField | None
java: JavaIRField | None
```
### 5.7 空值 IR 构造
`to_null_ir` 为缺失侧构造空 IR 记录(`JavaIRField("", "", "null", True)`),用于只有一侧有数据的场景。
## 6. 字段比对算法(field_compare.py
### 6.1 职责
对单个字段的 COBOL 值和 Java 值进行类型感知的比较,返回结构化的比对结果。
### 6.2 函数签名
```python
def compare_field(
name: str,
c: str, # COBOL 值
j: str, # Java 值
field_type: str = "decimal", # 字段类型
tolerance: float = 0.01 # 容忍度
) -> FieldResult
```
### 6.3 比对策略
| 字段类型 | 比对方式 | 说明 |
|----------|----------|------|
| `decimal` / `numeric` | 数值差 <= tolerance -> TOLERATED | 使用 `Decimal` 精确计算 |
| `date` | 8 位数字 -> `YYYY-MM-DD` 格式化后比较 | 统一格式消除表示差异 |
| `string` | strip 后直接比较 | 去除首尾空白 |
| 其他 | 字符串直接比较 | 兜底策略 |
### 6.4 状态判定
| 状态 | 条件 |
|------|------|
| `PASS` | 完全一致 |
| `TOLERATED` | 数值差在容忍度范围内 |
| `MISMATCH` | 不一致 |
| `NOT_SET` | 两侧均为空/None |
### 6.5 数值解析(_num
```python
def _num(v) -> Decimal | None
```
- `None` / `"None"` -> `None`
- 空字符串 -> `Decimal("0")`
-`\x00` -> 去除后解析
- 非数字字符串 -> `None`
使用 Python `Decimal` 进行精确十进制运算,避免浮点精度问题。
### 6.6 默认容忍度
```python
DEFAULT_TOLERANCE = 0.01
```
可通过 `Config.tolerance` 全局配置调整。
## 7. 舍入检测(rounding_detect.py
### 7.1 职责
判断两个数值之间的差异是否由 COBOL `ROUNDED` 子句引起,识别舍入模式并给出置信度。
### 7.2 函数签名
```python
def detect_rounding(c: str, j: str) -> RoundingResult
```
### 7.3 RoundingResult 数据类
```python
@dataclass
class RoundingResult:
mode: str = "EXACT" # 舍入模式
confidence: float = 1.0 # 置信度(0-1
suggestion: str = "" # 建议文本
```
### 7.4 检测算法
```
1. 解析 COBOL 值 (cv) 和 Java 值 (jv) 为 Decimal
2. 任一解析失败 -> UNKNOWN (confidence=0)
3. cv == jv -> EXACT (confidence=1.0)
4. 计算绝对差 diff = |cv - jv|
5. 判定模式:
- diff < 2 -> TRUNCATE (confidence=0.6)
- diff < 100 -> ROUNDING (confidence=0.4)
- diff >= 100 -> SIGNIFICANT (confidence=0.9)
```
### 7.5 舍入模式说明
| 模式 | 含义 | 置信度 | 建议 |
|------|------|--------|------|
| `EXACT` | 完全一致 | 1.0 | 无 |
| `TRUNCATE` | 截断差异(diff < 2 | 0.6 | 可能是 COBOL 截断行为 |
| `ROUNDING` | 舍入差异(diff < 100 | 0.4 | 可能存在舍入方式差异 |
| `SIGNIFICANT` | 显著差异(diff >= 100 | 0.9 | 需要关注的较大差异 |
### 7.6 数值解析(_d
```python
def _d(v) -> Decimal | None
```
- 使用 `Decimal(str(v).strip())` 解析
- 异常时返回 `None`
## 8. 接口定义
### 8.1 模块公开 API
```python
# comparator/__init__.py
align_records(cobol_records, java_records, key_field) -> list[tuple]
compare_field(name, c, j, field_type, tolerance) -> FieldResult
CobolBinaryReader # class
Normalizer # class
detect_rounding(c, j) -> RoundingResult
```
### 8.2 典型调用流程
```
cobol_binary_reader.read(path, field_tree) -> list[dict]
|
v
align_records(cobol_records, java_records, key_field) -> list[tuple]
|
v (对每个 MATCHED 对)
compare_field(name, cobol_val, java_val, field_type, tolerance) -> FieldResult
|
v (可选)
detect_rounding(cobol_val, java_val) -> RoundingResult
```
### 8.3 数据模型依赖
| 模型 | 定义位置 | 用途 |
|------|----------|------|
| `FieldTree` / `Field` | `data.field_tree` | 字段布局定义 |
| `FieldResult` | `data.diff_result` | 字段比对结果 |
| `IRRecord` / `CobolIRField` / `JavaIRField` | `comparator.normalizer` | 中间表示 |
| `RoundingResult` | `comparator.rounding_detect` | 舍入检测结果 |
## 9. 错误处理
### 9.1 错误策略
模块采用**严格解析 + 宽容比对**策略:
| 异常场景 | 处理方式 | 影响 |
|----------|----------|------|
| 二进制文件为空 | 返回空列表 | 无记录可比 |
| 记录不完整 | 跳过该记录 | 部分数据丢失 |
| COMP-3 空数据 | 返回 "0" | 默认零值 |
| EBCDIC 未知字符 | 替换为 `?` | 可能导致 MISMATCH |
| 数值解析失败 | 返回 `None` | NOT_SET 状态 |
| 两侧均无值 | NOT_SET | 不计入匹配统计 |
### 9.2 已知局限
1. `aligner.py` 仅支持单一主键,不支持复合主键
2. `CobolBinaryReader` 假定所有记录等长,不支持变长记录
3. `Normalizer.normalize_comp3` 不处理小数位(与 `CobolBinaryReader._comp3` 重复实现)
4. `detect_rounding` 的阈值(2/100)为经验值,未基于统计分析
5. `field_compare` 的字符串比较未处理 EBCDIC 与 ASCII 的编码差异
+474
View File
@@ -0,0 +1,474 @@
# 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 验证,依赖调用方保证格式正确
+825
View File
@@ -0,0 +1,825 @@
# 数据流设计文档
> 版本: 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 |
+26
View File
@@ -0,0 +1,26 @@
#!/usr/bin/env python3
"""Append the remaining sections to the design document."""
from pathlib import Path
OUT = Path(r"C:\Users\marye\Desktop\2026技术大赛\cobol-java-v3\docs\detailed-design\01-cobol-testgen-core.md")
content = OUT.read_text(encoding="utf-8")
content = content.replace("PLACEHOLDER", "")
# Read part2 content
part2 = Path(r"C:\Users\marye\Desktop\2026技术大赛\cobol-java-v3\docs\detailed-design\part2.md")
content += part2.read_text(encoding="utf-8")
# Read part3 content
part3 = Path(r"C:\Users\marye\Desktop\2026技术大赛\cobol-java-v3\docs\detailed-design\part3.md")
content += part3.read_text(encoding="utf-8")
# Read part4 content
part4 = Path(r"C:\Users\marye\Desktop\2026技术大赛\cobol-java-v3\docs\detailed-design\part4.md")
content += part4.read_text(encoding="utf-8")
# Read part5 content
part5 = Path(r"C:\Users\marye\Desktop\2026技术大赛\cobol-java-v3\docs\detailed-design\part5.md")
content += part5.read_text(encoding="utf-8")
OUT.write_text(content, encoding="utf-8")
print(f"Document complete: {len(content)} chars, {content.count(chr(10))+1} lines")
+345
View File
@@ -0,0 +1,345 @@
# 开发范式流程图
> 本文档定义 COBOL 迁移验证平台的开发工作流,用于展示团队的开发范式。
---
## 一、开发流程概览
```mermaid
graph TD
A[1. 需求分析] --> B[2. AI方案生成]
B --> C{3. 人工审核}
C -->|通过| D[4. AI编码实现]
C -->|需要修改| B
D --> E[5. 测试验证]
E --> F{6. 质量评审}
F -->|达标| G[7. 交付归档]
F -->|未达标| D
style A fill:#e1f5fe
style B fill:#f3e5f5
style C fill:#fff3e0
style D fill:#f3e5f5
style E fill:#e8f5e8
style F fill:#fff3e0
style G fill:#e8f5e8
```
### 流程说明
| 步骤 | 名称 | 负责人 | 说明 |
|:----:|------|:------:|------|
| 1 | 需求分析 | 人工 | 分析COBOL源码结构,明确迁移目标和验收标准 |
| 2 | AI方案生成 | AI | 利用AI分析代码,生成迁移方案和测试策略 |
| 3 | 人工审核 | 人工 | 审核AI方案的可行性和完整性 |
| 4 | AI编码实现 | AI | 根据方案生成代码、测试数据和配置 |
| 5 | 测试验证 | 工具+人工 | 运行测试,验证功能和覆盖率 |
| 6 | 质量评审 | 人工 | 评审测试结果,确认是否达标 |
| 7 | 交付归档 | 人工 | 整理交付物,归档项目文档 |
---
## 二、各阶段详细说明
### 1. 需求分析
**目标:** 理解COBOL程序的业务逻辑,明确迁移需求
**负责人:** 开发团队
**输入:**
- COBOL源代码
- 业务需求文档
- 迁移规范要求
**输出:**
- 需求分析报告
- 程序清单(含功能描述)
- 验收标准文档
**质量标准:**
- 需求覆盖率100%
- 程序分类准确(33+2种类型)
- 验收标准可量化
**活动:**
1. 阅读COBOL源码,理解业务逻辑
2. 识别程序类型(匹配系/键中断系/条件分支系等)
3. 分析数据流向和文件结构
4. 确定迁移目标和技术约束
5. 编写需求文档和验收标准
---
### 2. AI方案生成
**目标:** 利用AI生成迁移方案和测试策略
**负责人:** AIDeepSeek
**输入:**
- 需求分析报告
- COBOL源代码
- 程序分类结果
**输出:**
- 迁移方案文档
- 测试策略报告
- 技术选型建议
**质量标准:**
- 方案完整性检查
- 技术可行性评估
- 测试覆盖率目标明确
**活动:**
1. AI分析COBOL程序结构
2. 生成迁移路径建议
3. 设计测试策略(分支覆盖/MC/DC)
4. 推荐技术方案和工具
5. 输出方案文档供人工审核
---
### 3. 人工审核
**目标:** 确认AI方案的可行性和完整性
**负责人:** 开发团队
**输入:**
- AI生成的迁移方案
- 测试策略报告
**输出:**
- 审核意见(通过/修改建议)
- 最终确认的方案
**质量标准:**
- 技术可行性确认
- 风险识别完整
- 资源评估合理
**活动:**
1. 审核AI方案的技术合理性
2. 评估方案的可行性
3. 识别潜在风险和问题
4. 提出修改建议(如需要)
5. 确认最终方案
**决策点:**
- 通过 → 进入步骤4(AI编码实现)
- 需要修改 → 返回步骤2(AI方案生成)
---
### 4. AI编码实现
**目标:** 根据审核通过的方案生成代码和测试数据
**负责人:** AIDeepSeek
**输入:**
- 审核通过的迁移方案
- 需求文档
- COBOL源代码
**输出:**
- 迁移代码(Python/Java
- 测试数据文件
- 配置文件
- 单元测试脚本
**质量标准:**
- 代码规范检查通过
- 测试数据完整性验证
- 配置文件格式正确
**活动:**
1. AI根据方案生成代码
2. 创建测试数据和测试用例
3. 编写配置文件
4. 生成单元测试脚本
5. 输出代码供测试验证
---
### 5. 测试验证
**目标:** 验证代码功能和测试覆盖率
**负责人:** 工具+人工
**输入:**
- 迁移代码
- 测试数据
- 测试脚本
**输出:**
- 测试报告(通过/失败)
- 覆盖率报告(分支/语句)
- 比对结果报告
**质量标准:**
- 测试通过率≥95%
- 分支覆盖率≥80%
- 无严重缺陷
**活动:**
1. 运行单元测试(pytest
2. 执行集成测试
3. 收集覆盖率数据(gcov
4. 比对COBOL和Java输出
5. 生成测试报告
**工具:**
- pytestPython测试)
- gcov(覆盖率收集)
- 自定义比对脚本
---
### 6. 质量评审
**目标:** 评审测试结果,确认是否达标
**负责人:** 开发团队
**输入:**
- 测试报告
- 覆盖率报告
- 比对结果
**输出:**
- 评审意见(达标/未达标)
- 改进建议(如需要)
**质量标准:**
- 所有关键指标达标
- 无阻塞性问题
- 风险可控
**活动:**
1. 审查测试报告
2. 分析覆盖率数据
3. 确认比对结果
4. 识别未覆盖的分支
5. 做出质量决策
**决策点:**
- 达标 → 进入步骤7(交付归档)
- 未达标 → 返回步骤4(AI编码实现,反馈迭代)
---
### 7. 交付归档
**目标:** 整理交付物,归档项目文档
**负责人:** 开发团队
**输入:**
- 测试报告
- 覆盖率报告
- 代码和配置文件
- 需求文档
**输出:**
- 最终迁移报告
- 代码交付包
- 验证文档
- 项目归档
**质量标准:**
- 文档完整
- 代码可追溯
- 归档规范
**活动:**
1. 整理最终报告
2. 打包代码和配置
3. 编写交付说明
4. 归档项目文档
5. 完成项目总结
---
## 三、流程特点
### 3.1 人机协作
| 阶段 | 人/AI | 说明 |
|------|:-----:|------|
| 需求分析 | 人工 | 人工理解业务逻辑 |
| 方案生成 | AI | AI分析代码生成方案 |
| 方案审核 | 人工 | 人工确认可行性 |
| 编码实现 | AI | AI生成代码和测试 |
| 测试验证 | 工具 | 自动化测试执行 |
| 质量评审 | 人工 | 人工确认质量 |
| 交付归档 | 人工 | 人工整理交付 |
### 3.2 质量门禁
| 检查点 | 位置 | 标准 |
|--------|------|------|
| 方案完整性 | 步骤2→3 | 方案包含所有必要内容 |
| 技术可行性 | 步骤3 | 方案技术上可实现 |
| 代码规范 | 步骤4 | 代码符合规范要求 |
| 测试通过率 | 步骤5 | ≥95% |
| 分支覆盖率 | 步骤5 | ≥80% |
| 质量达标 | 步骤6 | 所有关键指标达标 |
### 3.3 反馈迭代
```
迭代循环:
步骤4 → 步骤5 → 步骤6 → 步骤4 (如未达标)
迭代次数:
通常 1-3 次
最多 5 次(超过需重新评估方案)
```
### 3.4 可审计性
| 特性 | 说明 |
|------|------|
| **步骤可追溯** | 每个步骤有明确的输入输出 |
| **角色可确认** | 每个步骤有明确的负责人 |
| **时间可记录** | 每个步骤的开始和结束时间 |
| **产出物可验证** | 每个步骤的产出物可检查 |
---
## 四、流程图(简化版)
```
需求分析 ──→ AI方案生成 ──→ 人工审核 ──→ AI编码实现
↑ │ │ │
│ │ │ │
│ ↓ │ ↓
│ (不通过) │ 测试验证
│ │ │ │
│ │ │ ↓
│ │ │ 质量评审
│ │ │ │
│ │ │ │ 达标
│ │ │ ↓
│ │ │ 交付归档
│ │ │ │
└──────────────┴──────────────┴──────────────┘
(未达标时反馈)
```
---
## 五、与AI使用日志的关系
本流程图定义了开发范式的各个步骤。在实际执行过程中,每个步骤的执行记录会体现在 `_AI_USAGE_LOG.md` 中,包含:
- 执行时间
- 执行步骤(对应本流程图的步骤名称)
- 修改摘要
- 涉及文件
- 使用的AI模型