# Spec-to-Code 需求覆盖验证报告 > 生成时间: 2026-06-09 > 对照 `openspec/specs/` 下 7 个 spec 文件 vs `sapcli/` 源码 --- ## 1. Connection (connection/spec.md) | # | Scenario | 状态 | 说明 | |---|----------|------|------| | 1.1 | Successful login | ✅已实现 | `client.py:login()` 使用 HTTP Basic Auth,`app.py:101-102` 打印"登录成功" | | 1.2 | Login failure | ✅已实现 | `client.py:62` raise `LoginError`,`app.py:144-146` 捕获 `SapCliError` 以非零退出码退出 | | 1.3 | Config not found | ✅已实现 | `config.py:25-29` `SAPConfig.__post_init__` 检查缺失字段抛 `ConfigError`,`app.py:91-95` 打印缺失提示和环境变量名称 | | 1.4 | Load from config file | ✅已实现 | `config.py:32-49` 搜索并读取 `config.ini` | | 1.5 | Environment variable override | ✅已实现 | `config.py:53-57` `os.environ.get("SAP_XXX", sap.get(...))` 环境变量优先 | | 1.6 | Profile selection (--profile) | ❌未实现 | `parser.py:69` 定义了 `--profile` 参数,但 `config.py:51` 硬编码 `cfg["SAP"]`,未使用 `args.profile` | | 1.7 | Client number default "100" | ✅已实现 | `config.py:55` `sap.get("client", "100")` | | 1.8 | Custom config path (--config) | ✅已实现 | `parser.py:67` + `app.py:88` 传入 `args.config` | | 1.9 | No command specified | ✅已实现 | `app.py:77-79` 检测 `not args.command` 打印帮助并以 exit(1) 退出 | **小计: 8/9 ✅, 1 ❌** --- ## 2. Object Lifecycle (object-lifecycle/spec.md) | # | Scenario | 状态 | 说明 | |---|----------|------|------| | 2.1 | Create with default template | ⚠️部分实现 | `crud.py:31-58` 仅有 report/class/function/interface 四种模板,其他 12 种类型无默认模板 | | 2.2 | Create with custom source file | ✅已实现 | `crud.py:690-695` `--source` 参数读取文件 | | 2.3 | Create DDIC with --definition | ✅已实现 | `crud.py:771-876` domain/dataelement/table/structure/tabletype 有 JSON→XML 转换 | | 2.4 | Create message class | ❌未实现 | `types.py` 未注册 `messageclass` 对象类型,无法 create/download/info/delete | | 2.5 | Create database view | ❌未实现 | `types.py` 未注册 `view` 对象类型 | | 2.6 | Create search help | ❌未实现 | `types.py` 未注册 `searchhelp` 对象类型 | | 2.7 | Create lock object | ❌未实现 | `types.py` 未注册 `lockobject` 对象类型 | | 2.8 | Create already existing object | ✅已实现 | `crud.py:642-644` 检测 `object_exists` 后抛 `ObjectAlreadyExistsError` | | 2.9 | Create with --corr_nr | ✅已实现 | `parser.py:110` + `crud.py:652-655` 传入传输请求号 | | 2.10 | Download existing object | ✅已实现 | `crud.py:73-122` 完整实现,文件名小写+.abap | | 2.11 | Download non-existent object | ✅已实现 | `crud.py:95-98` 检测不存在抛 `ObjectNotFoundError` | | 2.12 | Download function type (group/module) | ✅已实现 | `types.py:152-167` 解析 `组名/模块名` 格式 | | 2.13 | Download type without source | ✅已实现 | `crud.py:82-85` 检测 `src_uri is None` 报错 | | 2.14 | Download view DDL source | ❌未实现 | `view` 类型未在 `types.py` 注册 | | 2.15 | Full sync success (lock→write→unlock→check→activate) | ✅已实现 | `crud.py:226-317` 完整五步流程,每步报告进度 | | 2.16 | Auto-create on sync | ✅已实现 | `crud.py:213-224` 检测不存在则自动创建空对象 | | 2.17 | Syntax check failure | ✅已实现 | `crud.py:277-287` 报告行号和错误描述 | | 2.18 | Activation failure | ✅已实现 | `crud.py:308-317` 报告激活错误详情 | | 2.19 | Sync with --corr_nr | ✅已实现 | `crud.py:230-231` 贯穿整个流程 | | 2.20 | Query existing object info | ✅已实现 | `crud.py:371-449` 显示名称/类型/描述/状态/负责人等元数据 | | 2.21 | Query non-existent object | ✅已实现 | `crud.py:406-408` 404 检测 | | 2.22 | Query message class info | ❌未实现 | `messageclass` 未注册 | | 2.23 | Query view info | ❌未实现 | `view` 未注册 | | 2.24 | Delete with confirmation | ✅已实现 | `crud.py:483-486` input("yes") 确认 | | 2.25 | Delete cancelled | ✅已实现 | `crud.py:484-486` 非确认则取消 | | 2.26 | Delete non-existent object | ✅已实现 | `crud.py:474-477` 检测不存在抛 `ObjectNotFoundError` | | 2.27 | Delete message class | ❌未实现 | `messageclass` 未注册 | | 2.28 | Delete lock object | ❌未实现 | `lockobject` 未注册 | **小计: 16/28 ✅, 7 ❌, 1 ⚠️** ### 缺失对象类型汇总 以下 5 种对象类型在 spec 中列出但未在 `types.py` 中注册: - **messageclass** — 无 ObjectTypeConfig、无 URI 模板、无 create/download/info/delete - **view** — 无 ObjectTypeConfig、无 URI 模板 - **searchhelp** — 无 ObjectTypeConfig、无 URI 模板 - **lockobject** — 无 ObjectTypeConfig、无 URI 模板 - **include** — 无 ObjectTypeConfig(仅在 scanner.py 的 DIRECTORY_TYPE_MAP 中映射,但 parse_object_name 会报错) - **cdsview** — 无 ObjectTypeConfig(CDS 通过独立命令 `cds` 子系统处理,不走通用 CRUD 流程) --- ## 3. Batch Operations (batch-ops/spec.md) | # | Scenario | 状态 | 说明 | |---|----------|------|------| | 3.1 | Init new project | ✅已实现 | `batch.py:19-96` 扫描→查询→生成 manifest.json | | 3.2 | Manifest tracks objects | ✅已实现 | `manifest.py` 完整 CRUD + to_dict/from_dict 持久化 | | 3.3 | Query pending objects | ✅已实现 | `manifest.py:139-146` `pending_objects()` 方法 | | 3.4 | Scan standard directories | ✅已实现 | `scanner.py:13-29` 14 种目录映射 + `_scan_flat_directory` | | 3.5 | Function directory structure | ✅已实现 | `scanner.py:91-121` 子目录=组名,文件名=模块名 | | 3.6 | Skip hidden files | ✅已实现 | `scanner.py:77` `filename.startswith(".")` 跳过 | | 3.7 | Sort by dependency | ✅已实现 | `sorter.py:40-109` Kahn 算法拓扑排序 + TYPE_PRIORITY | | 3.8 | Detect cyclic dependency | ✅已实现 | `sorter.py:100-105` 抛 `CyclicDependencyError` 并显示循环链 | | 3.9 | Ignore external dependencies | ✅已实现 | `sorter.py:63-65` `dep not in names` 时忽略 | | 3.10 | Sync all objects (--all) | ✅已实现 | `batch.py:164-328` 完整批量同步 | | 3.11 | Dry run mode (--dry-run) | ✅已实现 | `batch.py:172,224-241` 仅显示计划不执行 | | 3.12 | Fail fast mode (--fail-fast) | ✅已实现 | `batch.py:173,287-289` 失败立即 break | | 3.13 | Refresh object status | ✅已实现 | `batch.py:99-161` 逐个查询 SAP 状态更新清单 | **小计: 13/13 ✅** --- ## 4. Search and Browse (search-browse/spec.md) | # | Scenario | 状态 | 说明 | |---|----------|------|------| | 4.1 | List by type with prefix | ⚠️部分实现 | `search.py:31-71` 命令层已实现,但调用 `client.list_objects()` — **client.py 中缺少该方法定义** | | 4.2 | List without filter | ⚠️部分实现 | 同上,命令层有,client 层缺失 | | 4.3 | Find references (whereused) | ⚠️部分实现 | `search.py:74-109` 命令层已实现,但调用 `client.where_used()` — **client.py 中缺少该方法定义** | | 4.4 | No references found | ⚠️部分实现 | `search.py:97-98` 显示无引用信息,但依赖缺失的 client 方法 | | 4.5 | Search by keyword | ⚠️部分实现 | `search.py:112-147` 命令层已实现,但调用 `client.search_code()` — **client.py 中缺少该方法定义** | | 4.6 | Search no results | ⚠️部分实现 | `search.py:136-137` 显示无结果信息,但依赖缺失的 client 方法 | | 4.7 | Diff with differences | ⚠️部分实现 | `diff_cmd.py:16-90` 命令层完整,但调用 `client.read_source_for_diff()` — **client.py 中缺少该方法定义** | | 4.8 | Diff identical | ⚠️部分实现 | `diff_cmd.py:70-72` 检测无差异,但依赖缺失的 client 方法 | | 4.9 | Download CDS view | ⚠️部分实现 | `cds.py:31-61` 命令层完整,但调用 `client.get_cds_source()` — **client.py 中缺少该方法定义** | | 4.10 | Create CDS view | ⚠️部分实现 | `cds.py:130-171` 命令层完整,但调用 `client.create_cds()` — **client.py 中缺少该方法定义** | | 4.11 | Query package info | ⚠️部分实现 | `package_cmd.py:56-77` 命令层完整,但调用 `client.get_package_info()` — **client.py 中缺少该方法定义** | | 4.12 | Create package | ⚠️部分实现 | `package_cmd.py:29-53` 命令层完整,但调用 `client.create_package()` — **client.py 中缺少该方法定义** | **小计: 0/12 ✅, 12 ⚠️ (命令层均已完成,但 ADTClient 缺少对应的 API 方法)** ### client.py 缺失的方法清单 以下方法被 commands/ 调用但未在 `client.py` 中定义: 1. `list_objects(obj_type, package, prefix)` — 被 search.py, package_cmd.py 调用 2. `where_used(name, obj_uri, adt_type)` — 被 search.py 调用 3. `search_code(query, obj_type)` — 被 search.py 调用 4. `read_source_for_diff(name, obj_type)` — 被 diff_cmd.py 调用 5. `get_cds_source(name)` — 被 cds.py 调用 6. `create_cds(name, description, ddl_source)` — 被 cds.py 调用 7. `create_package(name, description, superpackage)` — 被 package_cmd.py 调用 8. `get_package_info(name)` — 被 package_cmd.py 调用 --- ## 5. Transport Management (transport/spec.md) | # | Scenario | 状态 | 说明 | |---|----------|------|------| | 5.1 | List transport requests | ✅已实现 | `transport.py:32-56` + `client.py:249-280` `list_transport_requests()` | | 5.2 | Query transport info | ⚠️部分实现 | `transport.py:59-79` 命令层已实现,但调用 `client.transport_info()` — **client.py 中缺少该方法定义** | | 5.3 | Release transport request | ⚠️部分实现 | `transport.py:82-107` 命令层已实现,但调用 `client.transport_release()` — **client.py 中缺少该方法定义** | | 5.4 | List objects in transport | ⚠️部分实现 | `transport.py:110-136` 命令层已实现,但调用 `client.transport_objects()` — **client.py 中缺少该方法定义** | **小计: 1/4 ✅, 3 ⚠️** ### client.py 缺失的方法(Transport) 1. `transport_info(corr_nr)` — 传输请求详情查询 2. `transport_release(corr_nr)` — 释放传输请求 3. `transport_objects(corr_nr)` — 传输请求中的对象列表 --- ## 6. Code Quality (quality/spec.md) | # | Scenario | 状态 | 说明 | |---|----------|------|------| | 6.1 | Check with findings | ⚠️部分实现 | `quality.py:14-61` 命令层完整,但调用 `client.atc_check()` — **client.py 中缺少该方法定义** | | 6.2 | Check clean | ⚠️部分实现 | `quality.py:38-39` 显示通过信息,但依赖缺失的 client 方法 | | 6.3 | Format source code | ⚠️部分实现 | `quality.py:64-132` 命令层完整,但调用 `client.pretty_print()` — **client.py 中缺少该方法定义** | | 6.4 | Analyze project dependencies | ✅已实现 | `analyze.py:39-126` 本地源码解析,正则匹配 TYPE REF TO / CALL FUNCTION 等模式 | | 6.5 | Scaffold from template | ✅已实现 | `scaffold.py:198-239` 4 种模板(alv-report/bapi-wrapper/interface-class/data-model) | | 6.6 | List available templates | ❌未实现 | spec 要求不指定模板时列出可用模板,但 `parser.py:207` `--template` 为 `required=True`,无法不指定 | **小计: 2/6 ✅, 3 ⚠️, 1 ❌** ### client.py 缺失的方法(Quality) 1. `atc_check(name, obj_uri, variant)` — ATC 代码检查 2. `pretty_print(source)` — ABAP Pretty Printer --- ## 7. Configuration Management (config/spec.md) | # | Scenario | 状态 | 说明 | |---|----------|------|------| | 7.1 | Show current config | ✅已实现 | `config_cmd.py:20-40` 显示主机/客户端/用户名,密码掩码显示 | | 7.2 | Set config value | ❌未实现 | `parser.py:123-126` 未定义 `config set` 子命令,config_cmd.py 无 set 逻辑 | | 7.3 | List profiles | ✅已实现 | `config_cmd.py:43-66` `list-profiles` 子命令 | | 7.4 | Use specific profile | ❌未实现 | `--profile` 参数已定义但 `config.py` 硬编码使用 `[SAP]` section | | 7.5 | Save password to keyring | ⚠️部分实现 | `auth.py` 有 `set_password()` 和 `cmd_auth_login()`,但 **auth 模块未集成到 app.py 命令路由**,parser.py 也未注册 `auth` 子命令 | | 7.6 | Load password from keyring | ⚠️部分实现 | `auth.py:resolve_password()` 存在,但 **未被 config.py 或 app.py 调用** | **小计: 2/6 ✅, 2 ❌, 2 ⚠️** --- ## 汇总统计 | Spec 领域 | 总需求 | ✅已实现 | ❌未实现 | ⚠️部分实现 | 覆盖率 | |-----------|--------|---------|---------|-----------|--------| | Connection | 9 | 8 | 1 | 0 | 88.9% | | Object Lifecycle | 28 | 16 | 7 | 1 | 57.1% | | Batch Operations | 13 | 13 | 0 | 0 | 100% | | Search & Browse | 12 | 0 | 0 | 12 | 0% (命令层100%,API层0%) | | Transport | 4 | 1 | 0 | 3 | 25% | | Code Quality | 6 | 2 | 1 | 3 | 33.3% | | Config Management | 6 | 2 | 2 | 2 | 33.3% | | **总计** | **78** | **42** | **11** | **21** | **53.8%** | --- ## 关键缺失点总结 ### A. client.py 缺失的 13 个 ADT API 方法 这些方法被 commands/ 层调用但未实现,是最关键的缺口: | 方法 | 调用者 | 涉及 Spec | |------|--------|-----------| | `list_objects(obj_type, package, prefix)` | search.py, package_cmd.py | search-browse | | `where_used(name, obj_uri, adt_type)` | search.py | search-browse | | `search_code(query, obj_type)` | search.py | search-browse | | `read_source_for_diff(name, obj_type)` | diff_cmd.py | search-browse | | `get_cds_source(name)` | cds.py | search-browse | | `create_cds(name, description, ddl_source)` | cds.py | search-browse | | `create_package(name, description, superpackage)` | package_cmd.py | search-browse | | `get_package_info(name)` | package_cmd.py | search-browse | | `transport_info(corr_nr)` | transport.py | transport | | `transport_release(corr_nr)` | transport.py | transport | | `transport_objects(corr_nr)` | transport.py | transport | | `atc_check(name, obj_uri, variant)` | quality.py | quality | | `pretty_print(source)` | quality.py | quality | ### B. types.py 缺失的 5 个对象类型注册 | 类型 | Spec 要求 | 当前状态 | |------|----------|---------| | messageclass | create/download/info/delete | ❌ 未注册,无 URI | | view | create/download/info | ❌ 未注册,无 URI | | searchhelp | create | ❌ 未注册,无 URI | | lockobject | create/delete | ❌ 未注册,无 URI | | include | (扫描可用) | ❌ scanner.py 映射了目录但 types.py 未注册 | ### C. 配置/认证缺口 | 缺失项 | 详情 | |--------|------| | `--profile` 不可用 | parser.py 定义了参数但 config.py 忽略,始终读 [SAP] section | | `config set` 子命令 | spec 要求通过命令行设置配置项,parser.py 未注册,无实现 | | keyring 集成 | auth.py 模块完整但未被 app.py/config.py 引入调用链 | | scaffold 无模板列表 | `--template` 为 required,无法不指定来列出模板 |