关于 generate-rap-logic
此技能为现有 RAP 行为定义生成 determination、validation 和 action 的 ABAP Cloud 实现,面向 SAP RAP 开发者。它会检查行为池中的方法并结合文档模式生成代码,支持语法检查与激活验证。
主要功能
- 01读取 BDEF、CDS 视图和行为池
- 02识别空方法及缺失处理器签名
- 03生成 determination、validation 和 action 代码
- 04支持方法级写入、语法检查与激活
适用场景
- 01实现 RAP 行为池中的空处理方法
- 02为业务对象添加字段计算或默认值逻辑
- 03编写必填字段及跨字段校验
MCP 相关
技能通过 ARC-1 读取和修改 SAP 对象,并使用 mcp-sap-docs 查询 RAP 实现模式。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ arc-mcp/ arc-1 --skill generate-rap-logic安装后在 Claude Code 中输入/generate-rap-logic确认已加载。
详细介绍
生成 RAP 业务逻辑
为现有行为定义生成 RAP 确定逻辑、验证逻辑和自定义操作的实现。读取 RAP 堆栈,识别行为池中的空方法存根(或缺失的处理器签名),并生成 ABAP Cloud 实现代码。
此技能通过结合 ARC-1(SAP 系统访问)和 mcp-sap-docs(文档与最佳实践),复现 SAP Joule 的“RAP Logic Prediction”功能。
智能默认值(静默应用,切勿询问)
| 设置 | 默认值 | 原因 |
|---|---|---|
| 实体访问 | 始终使用 IN LOCAL MODE | 在行为池内部,无需进行授权检查 |
| 消息样式 | 使用 new_message_with_text() 编写硬编码文本 | 最适合原型开发;生产环境请使用 MSAG(参见“备注”) |
| 范围 | 所有空方法 | 用户看到列表后可以缩小范围 |
| 错误处理 | FAILED + REPORTED 结构 | RAP 错误传播的标准方式 |
输入
用户提供一个行为定义名称(例如 ZI_TRAVEL)。
只需提供行为定义名称。如果用户只提供 BDEF 名称,请应用智能默认值,继续读取堆栈并列出可用方法。
用户也可以选择指定:
- 特定的确定逻辑/验证逻辑(默认:列出全部并让用户选择)
- 行为描述 — 对所需逻辑的自然语言描述(例如“根据行项目计算总价”“验证状态转换”)
步骤 1:读取 RAP 堆栈
读取行为定义、CDS 视图和行为池,以了解完整的 RAP 上下文。
1a. 读取行为定义
SAPRead(type="BDEF", name="<bdef_name>")
⚠️ 快速失败关卡:如果读取 BDEF 时返回 404,对象可能不存在 — 请通过 SAPSearch 验证名称。如果返回 415,请使用 SAPManage(action="probe") 检查系统信息,并告知用户问题所在。
解析 BDEF 源代码以识别:
- 场景:托管/非托管/抽象
- 确定逻辑:
determination <Name> on (modify|save) { ... } - 验证逻辑:
validation <Name> on save { ... } - 操作:
action <Name> ... - 草稿状态:是否存在
with draft - 实体别名:
alias <Alias> - 持久化表:
persistent table <table_name> - 行为池类:
implementation in class <class_name> unique
1b. 读取接口 CDS 视图
SAPRead(type="DDLS", name="<interface_view>")
了解数据模型:字段名称、类型、别名和关联。这是生成正确的 READ ENTITIES 和 MODIFY ENTITIES 语句所必需的。
如果存在访问控制,也请读取它,以了解 CDS 级别的授权约束:
SAPRead(type="DCLS", name="<interface_view>_DCL")
1c. 获取依赖上下文
SAPContext(type="DDLS", name="<interface_view>", action="deps")
了解底层表、关联和相关实体。这有助于执行跨实体验证,或读取关联数据的确定逻辑。
1d. 读取行为池类
从 BDEF 源代码中找到行为池类名称(implementation in class <name>),然后:
SAPRead(type="CLAS", name="<bp_class>", format="structured")
默认使用结构化读取,因为它会在一次响应中返回元数据、包含文件和现有测试类。如果之后需要简洁的方法目录,可添加:
SAPRead(type="CLAS", name="<bp_class>", method="*")
识别哪些方法是空存根(方法体只有注释、RETURN 或为空),以及哪些 BDEF 声明的处理器完全未出现在类签名中。
1e. 可选的历史记录/文档上下文
如果这是一个已由他人修改过的现有 RAP BO,请在覆盖逻辑前检查近期更改和附带的文档:
SAPRead(type="VERSIONS", name="<bp_class>", objectType="CLAS")
SAPRead(type="SKTD", name="<bp_class>")
步骤 2:确定目标方法
解析 BDEF,找出确定逻辑和验证逻辑声明。展示摘要表:
Methods in behavior pool ZBP_I_TRAVEL:
| # | Type | Name | Trigger | Fields | Status |
|---|---------------|-----------------------|--------------------|-----------------|-------------|
| 1 | Determination | calculateTotalPrice | on modify | Price, Quantity | empty |
| 2 | Determination | setDefaultStatus | on modify | %create | empty |
| 3 | Validation | validateStatus | on save | Status | implemented |
| 4 | Validation | validateDates | on save | BeginDate | empty |
| 5 | Validation | validateCustomer | on save | CustomerID | empty |
询问用户:“我应该实现哪些方法?(所有空方法/指定编号/跳过某些方法?)”
如果用户提供了自然语言描述,请将其映射到相应的方法。
2b. 缺失处理程序签名恢复
如果 BDEF 声明了类定义中不存在的 action/determination/validation 处理程序:
- 运行
SAPWrite(action="scaffold_rap_handlers", type="CLAS", name="<bp_class>", bdefName="<bdef_name>"),列出缺失的签名。 - 如果存在缺失签名,使用
autoApply=true再次运行,以便在可能的情况下将声明和空方法存根注入类的各个区段。 - 如果仍未解决,请尝试 MCP 快速修复流程:
SAPDiagnose(action="quickfix", ...)SAPDiagnose(action="apply_quickfix", ...)
- 如果仍未解决,请使用 ADT 快速修复生成缺失的
METHODS ... FOR ...签名。 - 在编写方法体之前,使用
SAPRead(type="CLAS", name="<bp_class>", method="*")重新读取方法列表。
第 3 步:研究 RAP 模式
使用 mcp-sap-docs 获取最新的 RAP 实现模式。先进行参考资料式查询(includeSamples=false),仅在需要可执行模式时再获取示例(includeSamples=true):
search(query="RAP validation implementation ABAP example", includeSamples=true, abapFlavor="<cloud|standard>")
search(query="RAP determination on save trigger", includeSamples=false, abapFlavor="<cloud|standard>")
针对特定逻辑模式:
search(query="RAP calculate total price determination", includeSamples=true, abapFlavor="<cloud|standard>")
search(query="RAP status validation transition", includeSamples=true, abapFlavor="<cloud|standard>")
对于经常伴随逻辑变更的后端驱动型 UI 行为:
search(query="RAP feature control side effects authorization control", includeSamples=false, abapFlavor="<cloud|standard>")
参考文档,确保采用正确的 ABAP Cloud 模式:
- 使用
READ ENTITIES OF <entity> IN LOCAL MODE读取实体数据 - 使用
MODIFY ENTITIES OF <entity> IN LOCAL MODE更新实体数据 - 正确处理
FAILED/REPORTED结构 - 使用正确的方法签名区分 determination 和 validation
- 使用
IN LOCAL MODE的 EML 不会评估特性控制检查
第 4 步:生成方法实现
为每个选定的方法生成 ABAP Cloud 实现代码。
Determination 模板
METHOD <determination_name>.
" Read relevant fields of all affected entities
READ ENTITIES OF <interface_view> IN LOCAL MODE
ENTITY <alias>
FIELDS ( <trigger_fields> )
WITH CORRESPONDING #( keys )
RESULT DATA(lt_entities).
" Calculate derived values
LOOP AT lt_entities ASSIGNING FIELD-SYMBOL(<entity>).
" --- Business logic here ---
" Example: <entity>-TotalPrice = <entity>-Price * <entity>-Quantity.
ENDLOOP.
" Write back modified fields
MODIFY ENTITIES OF <interface_view> IN LOCAL MODE
ENTITY <alias>
UPDATE FIELDS ( <modified_fields> )
WITH VALUE #( FOR entity IN lt_entities
( %tky = entity-%tky
<field> = entity-<field> ) )
REPORTED DATA(lt_update_reported).
reported = CORRESPONDING #( DEEP lt_update_reported ).
ENDMETHOD.
Validation 模板
METHOD <validation_name>.
" Read relevant fields of all affected entities
READ ENTITIES OF <interface_view> IN LOCAL MODE
ENTITY <alias>
FIELDS ( <validated_fields> )
WITH CORRESPONDING #( keys )
RESULT DATA(lt_entities).
LOOP AT lt_entities ASSIGNING FIELD-SYMBOL(<entity>).
" --- Validation logic here ---
" Example: check field is not initial
IF <entity>-<Field> IS INITIAL.
APPEND VALUE #( %tky = <entity>-%tky ) TO failed-<alias>.
APPEND VALUE #(
%tky = <entity>-%tky
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = '<Error message>' )
%element-<Field> = if_abap_behv=>mk-on
) TO reported-<alias>.
ENDIF.
ENDLOOP.
ENDMETHOD.
Action 模板
METHOD <action_name>.
READ ENTITIES OF <interface_view> IN LOCAL MODE
ENTITY <alias>
FIELDS ( <action_input_fields> )
WITH CORRESPONDING #( keys )
RESULT DATA(lt_entities).
LOOP AT lt_entities ASSIGNING FIELD-SYMBOL(<entity>).
" --- Action logic here ---
ENDLOOP.
MODIFY ENTITIES OF <interface_view> IN LOCAL MODE
ENTITY <alias>
UPDATE FIELDS ( <action_output_fields> )
WITH VALUE #( FOR entity IN lt_entities
( %tky = entity-%tky
<field> = entity-<field> ) )
REPORTED DATA(lt_reported)
FAILED DATA(lt_failed).
reported = CORRESPONDING #( DEEP lt_reported ).
failed = CORRESPONDING #( DEEP lt_failed ).
ENDMETHOD.
向用户展示
展示每个方法生成的代码,并询问:
“以下是生成的实现。要将它写入 SAP 系统吗?(是/先编辑/取消)”
如果用户希望编辑,请先纳入修改,再继续操作。
第 5 步:写入并验证
写入之前,可以先对生成的代码进行 lint 检查,在获取 SAP 锁之前发现问题:
SAPLint(action="lint", source="<generated_method_code>", name="<bp_class>")
如果希望生成的方法体在写回之前遵循 SAP 的格式化程序设置:
SAPLint(action="format", source="<generated_method_code>", name="<bp_class>")
调用 edit_method 前,请确认目标方法存在于 SAPRead(..., method="*") 的结果中。如果该方法尚不存在,请先运行 scaffold_rap_handlers,再尝试快速修复流程(quickfix + apply_quickfix),或使用 ADT 快速修复作为后备方案。
使用方法级手术方式写入每个方法的实现:
SAPWrite(action="edit_method", type="CLAS", name="<bp_class>", method="<method_name>", source="<generated_code>", transport="<transport>")
注意: transport 参数对 edit_method 的使用有帮助,但并非始终必需。如果未提供显式 transport,ARC-1 会自动传递由锁提供的 corrNr。启用时(默认开启),写入前会自动进行 lint 验证。修改行为池时,优先使用 edit_method,而不是重写整个类。
写入所有方法后,运行语法检查:
SAPRead(type="SYNTAX", objectType="CLAS", name="<bp_class>")
如果出现语法错误,请先尝试 SAP 快速修复建议,再手动编辑:
SAPDiagnose(action="quickfix", type="CLAS", name="<bp_class>", source="<current_source>", line=<error_line>, column=<error_col>)
SAP 快速修复可以自动解决常见问题,例如缺少声明或简单的语法修正。
如果出现语法错误:
- 仔细阅读错误消息
- 常见问题:
READ ENTITIES中的实体名称错误、字段别名不正确、缺少IN LOCAL MODE - 修复方法并重新写入
- 重新检查语法
第 6 步:激活并验证
同时激活行为池和行为定义:
SAPActivate(objects=[{type:"BDEF", name:"<bdef>"}, {type:"CLAS", name:"<bp_class>"}])
注意: 激活会返回结构化响应,其中包含详细的错误/警告消息,包括行号和 URI。利用这些信息精确定位问题,而不必重新读取完整源代码。
可选:如果存在测试类,则运行单元测试:
SAPDiagnose(action="unittest", type="CLAS", name="<bp_class>")
如果可以使用 ATC,且此次变更并非简单变更,也运行 ATC:
SAPDiagnose(action="atc", type="CLAS", name="<bp_class>")
提供摘要:
RAP Logic Generation Complete!
Implemented methods:
[x] Determination: calculateTotalPrice — calculates total from price * quantity
[x] Determination: setDefaultStatus — sets status to 'N' (New) on create
[x] Validation: validateDates — ensures begin_date < end_date
[x] Validation: validateCustomer — checks customer_id is not empty
[ ] Validation: validateStatus — already implemented, skipped
Next steps:
- Test the logic via the Fiori Elements preview
- Add unit tests (use generate-abap-unit-test skill)
- Add more validations/determinations to the BDEF as needed
常见 RAP 逻辑模式
字段计算确定
根据其他字段计算派生字段(例如:总价 = 价格 * 数量):
METHOD calculateTotalPrice.
READ ENTITIES OF zi_order IN LOCAL MODE
ENTITY Order
FIELDS ( Price Quantity )
WITH CORRESPONDING #( keys )
RESULT DATA(lt_orders).
LOOP AT lt_orders ASSIGNING FIELD-SYMBOL(<order>).
<order>-TotalPrice = <order>-Price * <order>-Quantity.
ENDLOOP.
MODIFY ENTITIES OF zi_order IN LOCAL MODE
ENTITY Order
UPDATE FIELDS ( TotalPrice )
WITH VALUE #( FOR order IN lt_orders
( %tky = order-%tky
TotalPrice = order-TotalPrice ) )
REPORTED DATA(lt_reported).
reported = CORRESPONDING #( DEEP lt_reported ).
ENDMETHOD.
默认值确定
创建实体时设置默认值:
METHOD setDefaultStatus.
READ ENTITIES OF zi_travel IN LOCAL MODE
ENTITY Travel
FIELDS ( Status )
WITH CORRESPONDING #( keys )
RESULT DATA(lt_travels).
MODIFY ENTITIES OF zi_travel IN LOCAL MODE
ENTITY Travel
UPDATE FIELDS ( Status )
WITH VALUE #( FOR travel IN lt_travels
WHERE ( Status IS INITIAL )
( %tky = travel-%tky
Status = 'N' ) )
REPORTED DATA(lt_reported).
reported = CORRESPONDING #( DEEP lt_reported ).
ENDMETHOD.
必填字段验证
检查必填字段是否为空:
METHOD validateCustomer.
READ ENTITIES OF zi_travel IN LOCAL MODE
ENTITY Travel
FIELDS ( CustomerID )
WITH CORRESPONDING #( keys )
RESULT DATA(lt_travels).
LOOP AT lt_travels ASSIGNING FIELD-SYMBOL(<travel>).
IF <travel>-CustomerID IS INITIAL.
APPEND VALUE #( %tky = <travel>-%tky ) TO failed-travel.
APPEND VALUE #(
%tky = <travel>-%tky
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = 'Customer must be specified' )
%element-CustomerID = if_abap_behv=>mk-on
) TO reported-travel.
ENDIF.
ENDLOOP.
ENDMETHOD.
跨字段验证
验证相关字段是否一致(例如:开始日期早于结束日期):
METHOD validateDates.
READ ENTITIES OF zi_travel IN LOCAL MODE
ENTITY Travel
FIELDS ( BeginDate EndDate )
WITH CORRESPONDING #( keys )
RESULT DATA(lt_travels).
LOOP AT lt_travels ASSIGNING FIELD-SYMBOL(<travel>).
IF <travel>-BeginDate IS NOT INITIAL
AND <travel>-EndDate IS NOT INITIAL
AND <travel>-BeginDate > <travel>-EndDate.
APPEND VALUE #( %tky = <travel>-%tky ) TO failed-travel.
APPEND VALUE #(
%tky = <travel>-%tky
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = 'Begin date must be before end date' )
%element-BeginDate = if_abap_behv=>mk-on
%element-EndDate = if_abap_behv=>mk-on
) TO reported-travel.
ENDIF.
ENDLOOP.
ENDMETHOD.
状态转换验证
验证状态变更是否遵循允许的转换规则:
METHOD validateStatus.
READ ENTITIES OF zi_travel IN LOCAL MODE
ENTITY Travel
FIELDS ( Status )
WITH CORRESPONDING #( keys )
RESULT DATA(lt_travels).
LOOP AT lt_travels ASSIGNING FIELD-SYMBOL(<travel>).
CASE <travel>-Status.
WHEN 'N' OR 'O' OR 'A' OR 'X'.
" Valid statuses: New, Open, Accepted, Cancelled
WHEN OTHERS.
APPEND VALUE #( %tky = <travel>-%tky ) TO failed-travel.
APPEND VALUE #(
%tky = <travel>-%tky
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = |Invalid status: { <travel>-Status }| )
%element-Status = if_abap_behv=>mk-on
) TO reported-travel.
ENDCASE.
ENDLOOP.
ENDMETHOD.
编号范围确定
为非 UUID 键场景分配连续编号:
METHOD setTravelID.
READ ENTITIES OF zi_travel IN LOCAL MODE
ENTITY Travel
FIELDS ( TravelID )
WITH CORRESPONDING #( keys )
RESULT DATA(lt_travels).
" Get next number from number range or max+1
SELECT MAX( travel_id ) FROM ztravel_d INTO @DATA(lv_max_id).
DATA(lv_next_id) = lv_max_id + 1.
LOOP AT lt_travels ASSIGNING FIELD-SYMBOL(<travel>)
WHERE TravelID IS INITIAL.
<travel>-TravelID = lv_next_id.
lv_next_id += 1.
ENDLOOP.
MODIFY ENTITIES OF zi_travel IN LOCAL MODE
ENTITY Travel
UPDATE FIELDS ( TravelID )
WITH VALUE #( FOR travel IN lt_travels
WHERE ( TravelID IS NOT INITIAL )
( %tky = travel-%tky
TravelID = travel-TravelID ) )
REPORTED DATA(lt_reported).
reported = CORRESPONDING #( DEEP lt_reported ).
ENDMETHOD.
错误处理
常见问题和修复方法
| 错误 | 原因 | 修复方法 |
|---|---|---|
| DDLS/BDEF 上出现 415 Unsupported Media Type | RAP/CDS 端点未按预期响应 | 检查 SAPManage(action="probe") 获取系统信息。确认 ICF 服务已激活。尝试在 ADT 中创建对象,以确认系统功能。 |
| 在行为池中找不到方法 | BDEF 中的类名与实际类不匹配 | 检查 BDEF 源码中的 implementation in class,确认类存在 |
缺少 METHODS ... FOR ... 处理程序签名 | BDEF 声明已存在,但尚未生成类签名 | 先运行 SAPWrite(action="scaffold_rap_handlers", ...)(可选设置 autoApply=true),然后运行 SAPDiagnose(action="quickfix") + apply_quickfix 或使用 ADT 快速修复,再重试 edit_method |
语法错误:READ ENTITIES 中的 <entity> 未知 | 实体名称或别名错误 | 使用 BDEF 中 define behavior for ... alias <Alias> 的确切别名 |
语法错误:字段 <Field> 未知 | 字段别名与 CDS 视图不匹配 | 检查 CDS 视图字段别名——BDEF 使用 CDS 别名,而非表字段名称 |
| 激活失败 | BDEF 和类不兼容 | 同时激活 BDEF 和类:SAPActivate(objects=[...]) |
FAILED / REPORTED 结构不匹配 | 在 failed-<alias> 或 reported-<alias> 中使用了错误的别名 | 使用 BDEF 中的小写实体别名(例如,使用 failed-travel,而非 failed-Travel) |
缺少 IN LOCAL MODE | 缺少该子句会导致授权检查 | 在行为池内部读取/写入时,始终使用 IN LOCAL MODE |
%tky 不可用 | 方法签名未提供事务键 | 检查方法签名——determination 使用 keys,validation 使用 keys |
MODIFY ENTITIES 出现运行时错误 | 尝试修改只读字段 | 不要修改 BDEF 中标记为 field ( readonly ) 的字段 |
完整类写入时出现通用保存错误 [?/011] | 某些系统上的行为池完整类更新路径不稳定 | 使用 scaffold_rap_handlers + quickfix 路径生成签名,然后仅通过 edit_method 修改方法体 |
注意事项
BTP 与本地部署版本的差异
- BTP:严格执行 ABAP Cloud 语法。只能使用
READ ENTITIES/MODIFY ENTITIES,不能直接从数据库表中执行SELECT。所有 API 都必须已发布。BDEF 中使用strict ( 2 )。 - 本地部署版本:更灵活——行为池中可以使用经典 ABAP(例如,直接执行
SELECT),但强烈建议使用READ ENTITIES/MODIFY ENTITIES,以确保一致性和未来兼容性。 - 授权分层:
DCLS强制执行 CDS 行级访问控制,而 BDEF 授权(authorization master)负责行为操作。调试与授权相关的逻辑时,请同时考虑两者。
此技能不会做什么
- 自动编写 BDEF 声明:此技能假设 BDEF 中已有声明;不会为你重新设计 BDEF 契约。
- 副作用:不提供端到端的
side effects实现(UI 刷新触发器)。如有需要,请手动添加。 - 特性控制:不提供端到端的动态特性控制(
instance_features/global_features)。请手动添加,尤其是在 UI 应禁用操作或更新时。 - 授权:不提供
authorization master实现。请手动添加授权检查。 - 跨 BO 逻辑:不提供业务对象间操作。每个 determination/validation 都在其自身的 BO 内运行。
- 消息类创建:消息使用硬编码文本。对于生产服务,请随后创建消息类:
SAPWrite(action="create", type="MSAG", name="Z<MSG_CLASS>", ...),并将new_message_with_text()替换为引用该消息类的new_message()。
何时使用此技能
- 创建 RAP 服务后(先使用 generate-rap-service)
- BDEF 中已有 determination/validation 声明,但方法存根为空时
- 为现有 RAP 服务添加新业务逻辑时
- 用户用自然语言描述所需行为时(例如:“验证结束日期晚于开始日期”)
- NOT 用于从头创建 RAP 服务(请使用 generate-rap-service)
- NOT 用于 CDS 单元测试(请使用 generate-cds-unit-test 或 generate-abap-unit-test)