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

258 lines
7.8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: 修改代码 - 原理
created: 2026-05-18
tags:
- SAP
- ADT
- edit
- lock
- activation
- REST
parent: "[[sap-cli/README|SAP ADT 学习笔记总览]]"
prev: "[[sap-cli/02.查询代码-原理|02.查询代码 - 原理]]"
next: "[[sap-cli/04.检查代码-原理|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
```
### 响应
```xml
<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 调用序列:
```python
# 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 权限 |
## 🔗 相关笔记
- [[sap-cli/02.查询代码-原理|02.查询代码 - 原理]]
- [[sap-cli/04.检查代码-原理|04.检查代码 - 原理]]
## 📚 参考来源
- [SAP Help Portal - ADT User Guide](https://help.sap.com/docs/abap-cloud/abap-development-tools-user-guide/about-abap-development-tools-user-guide)
- [End-to-End SAP Automation with ADT REST Services](https://medium.com/@onuryz.itu/end-to-end-sap-automation-with-adt-rest-services-and-ai-a-modern-alternative-to-gui-scripting-343ea86064ea)
- [erpl-adt (GitHub)](https://github.com/DataZooDE/erpl-adt)