Files
sap-cli-skill/docs/adt/03.修改代码-原理.md
吴让宇 c5905a5b1e refactor: 本仓升为唯一源(原 sap-cli 源码仓归档)
方向反转:此前 SKILL.md 是「模板渲染产物」、sap-cli 是源;现 sap-cli 归档,
sap-cli-skill 承接开发与分发,SKILL.md 回归手工维护的正本。

迁移(来自 sap-cli,共 104 文件):
- tests/           692 例测试(15 个文件的内联 sys.path 改指 assets/)
- openspec/        SDD 规格与归档变更(42 文件)
- docs/            开发文档与 ADT 原理(含 dev/CLAUDE.md、AGENTS.md)
- .claude/         rules 副本 + settings.json(供 Claude Code)
- .github/ .hermes/ .pre-commit-config.yaml .editorconfig CLAUDE.md
- scripts/ 保持仅 setup.py(pack_skill.py 已随旧仓归档,不迁)

修复(迁移暴露的真实缺陷):
- assets/pyproject.toml 的 build-backend 写作 `setuptools.backends._legacy:_Backend`,
  该模块在 setuptools 中不存在 → `pip install -e` 从来装不上。改为 build_meta。
  实测:临时 venv 安装成功,sap-cli --help 正常列出 31 个命令
- pyproject readme 指向不存在的 assets/README.md(editable 安装会失败)→ 改内联文本
- pyproject urls 改指 sap-cli-skill

机制调整:
- .github/workflows/ci.yml 适配 assets/ 布局;顶部注明该工作流仅 GitHub 执行,
  本仓在 Gitee 不会自动跑
- pre-commit 增本地测试门禁(Gitee 上真正生效的那道)
- .gitignore 合并旧仓完整规则(保留 log/ 下 md 知识库入库,只忽略运行日志)
- 大文件上限 100KB→1MB(架构图 512KB)

守卫测试 tests/unit/test_repo_guards.py(10 → 18 例):
- SKILL.md 须记录 parser 全部 CLI 命令 / 铁律 1-5 须为真实小节标题 / 示例不得违反铁律 5
- references/ 规则齐备;.claude/rules 与 references 必须一致(实测抓到一次真实漂移)
- VERSION == sapcli.__version__ == README 版本
- 仓内不得再出现 pack_skill.py / skill-src(防废弃流程回潮)

698 tests OK;editable 安装与 CLI 入口经临时 venv 实测通过。
docs/RELEASING.md 重写为单源开发流程。
2026-09-11 00:40:15 +08:00

7.8 KiB
Raw Permalink Blame History

title, created, tags, parent, prev, next
title created tags parent prev next
修改代码 - 原理 2026-05-18
SAP
ADT
edit
lock
activation
REST
sap-cli/README sap-cli/02.查询代码-原理 sap-cli/04.检查代码-原理

修改代码 — 原理

[!abstract] 核心流程 ADT 中修改代码遵循 锁定 → 编辑 → 保存 → 解锁 → 激活 的严格工作流。每一步都通过 REST API 调用对应的 ABAP 后端服务完成。

1. 完整工作流

┌─────────┐    ┌─────────┐    ┌─────────┐    ┌─────────┐    ┌─────────┐
│  1.锁定   │───▶│  2.编辑   │───▶│  3.保存   │───▶│  4.解锁   │───▶│  5.激活   │
│  Lock    │    │  Edit   │    │  Save   │    │  Unlock │    │ Activate│
└─────────┘    └─────────┘    └─────────┘    └─────────┘    └─────────┘
     │              │              │              │              │
     ▼              ▼              ▼              ▼              ▼
  POST lock     GET source     PUT source     POST unlock    POST activate
  → lockHandle  → 纯文本       + lockHandle   + lockHandle   → 激活结果

[!warning] 重要约束

  • 必须先锁定 才能修改代码,否则 PUT 请求会被拒绝
  • 锁定句柄 (lockHandle) 是贯穿整个编辑流程的关键凭证
  • 必须激活 后代码才会在运行时生效(保存 ≠ 激活)

2. Step 1: 锁定对象 (Lock)

原理

ABAP 使用 乐观锁 (Optimistic Lock) 机制。当开发者打开一个对象进行编辑时,ADT 向后端发送锁定请求,获取一个唯一的 lockHandle

REST API 调用

POST /sap/bc/adt/oo/classes/{class_name}
     ?_action=LOCK
     &accessMode=MODIFY

响应

<adtcore:object xmlns:adtcore="http://www.sap.com/adt/core"
                LOCK_HANDLE="0123456789ABCDEF"/>
  • 返回的 LOCK_HANDLE 是一个唯一标识符
  • 后续的写入操作必须携带此句柄
  • 锁定是 会话级 的,HTTP 会话断开后锁自动释放

后端处理

1. ADT Framework 接收锁定请求
   ↓
2. 调用 ABAP 锁定管理器 (ENQUEUE)
   ↓
3. 检查对象是否已被其他用户锁定
   ↓
4. 如果未锁定 → 创建锁定条目,返回 lockHandle
5. 如果已锁定 → 返回错误(锁定冲突)

对应 SAP GUI

ADT 操作 SAP GUI 等效
Lock 对象 打开 SE24/SE38 编辑模式时自动锁定

3. Step 2: 读取源代码 (Read Source)

REST API 调用

GET /sap/bc/adt/oo/classes/{class_name}/source/main
Accept: text/plain

对象路径模式

不同对象类型使用不同的路径:

对象类型 路径模板
oo/classes/{name}/source/main
程序 programs/programs/{name}/source/main
函数模块 functions/groups/{group}/fmodules/{fm}/source/main
CDS View ddic/cds/views/{name}/source/main

响应

直接返回 纯文本 格式的 ABAP 源代码(Content-Type: text/plain)。

4. Step 3: 写入源代码 (Write Source)

REST API 调用

PUT /sap/bc/adt/oo/classes/{class_name}/source/main
Content-Type: text/plain; charset=utf-8
X-sap-adt-lockhandle: {lockHandle}

<修改后的 ABAP 源代码>

[!important] 关键要点

  • 请求体是 纯文本(不是 XML
  • 必须携带 X-sap-adt-lockhandle Header
  • 保存 ≠ 激活:代码已写入仓库但仍处于 非活跃状态

后端处理

1. 验证 lockHandle 是否有效
   ↓
2. 将源代码写入 ABAP Repository 的非活跃版本 (Inactive Version)
   ↓
3. 对象状态变为 "Modified"(在 Project Explorer 中显示 * 标记)
   ↓
4. 记录到传输请求任务中(如果已分配)

与 SAP GUI 的对比

特性 SAP GUI (SE80/SE38) ADT (Eclipse)
保存行为 保存即激活(或保存为非活跃) 保存 ≠ 激活(显式分离)
锁定管理 隐式(打开编辑时自动锁定) 显式(REST API 锁定/解锁)
版本管理 版本数据库 本地历史 + 版本数据库

5. Step 4: 解锁对象 (Unlock)

REST API 调用

POST /sap/bc/adt/oo/classes/{class_name}
     ?_action=UNLOCK
     &lockHandle={lockHandle}

解锁后,其他开发者可以锁定并编辑该对象。

6. Step 5: 激活对象 (Activate)

原理

[!tip] 激活 vs 保存

  • 保存 (Save):将源代码写入仓库的非活跃版本
  • 激活 (Activate):编译代码并生成可执行版本(Active Version
  • 只有激活后的代码才能被运行时系统使用

REST API 调用

POST /sap/bc/adt/activation
Content-Type: application/xml

<?xml version="1.0" encoding="utf-8"?>
<adtcore:objectReferences
  xmlns:adtcore="http://www.sap.com/adt/core">
  <adtcore:objectReference
    adtcore:name="{object_name}"
    adtcore:uri="/sap/bc/adt/oo/classes/{class_name}"/>
</adtcore:objectReferences>

后端激活流程

1. ADT 激活框架接收激活请求
   ↓
2. 执行预检查 (Pre-audit)
   ├── 语法检查 (Syntax Check)
   ├── 依赖检查(引用的对象是否已激活)
   └── 权限检查
   ↓
3. 如果预检查通过:
   ├── 编译 ABAP 代码 → 生成 ABAP Load
   ├── 更新 ABAP Repository 中的活跃版本 (Active Version)
   ├── 触发 ATC 检查(如果配置了自动检查)
   └── 将活跃版本写入传输请求
   ↓
4. 如果预检查失败:
   └── 返回错误列表(语法错误、缺失引用等)

对应 SAP GUI

ADT 操作 SAP GUI 等效
激活单个对象 SE80 → Activate (Ctrl+F3)
批量激活 SE80 → Inactive Objects → Activate All

7. 完整 REST API 调用序列示例

以下是一个修改 ABAP 类的完整 REST API 调用序列:

# 1. 锁定对象
lock_resp = POST("/sap/bc/adt/oo/classes/zcl_example",
                  _action="LOCK", accessMode="MODIFY")
lock_handle = parse_lock_handle(lock_resp)

# 2. 读取当前源代码
source = GET("/sap/bc/adt/oo/classes/zcl_example/source/main",
             Accept="text/plain")

# 3. 修改源代码(客户端操作)
modified_source = modify_source(source)

# 4. 写入修改后的源代码
PUT("/sap/bc/adt/oo/classes/zcl_example/source/main",
    body=modified_source,
    headers={"X-sap-adt-lockhandle": lock_handle})

# 5. 解锁对象
POST("/sap/bc/adt/oo/classes/zcl_example",
     _action="UNLOCK", lockHandle=lock_handle)

# 6. 激活对象
POST("/sap/bc/adt/activation",
     body=activation_xml("ZCL_EXAMPLE",
            "/sap/bc/adt/oo/classes/zcl_example"))

8. 错误处理

场景 HTTP 状态码 处理方式
对象已被锁定 403/409 提示用户等待或强制解锁
CSRF Token 过期 403 自动重新获取 Token 并重试
语法错误 400 返回错误列表,阻止激活
权限不足 401/403 检查 S_DEVELOP 权限

🔗 相关笔记

📚 参考来源