mcpField
A
abap-rap

abap-rap

github.com/dankromp开发837 次安装9 周前更新
代码仓库

关于 abap-rap

abap-rap 是面向 SAP ABAP RESTful Application Programming Model(RAP)开发者的最佳实践参考。内容涵盖分层架构、实现方式选择、关键对象及常见错误。

主要功能

  1. 01RAP 三层架构
  2. 02托管与非托管实现建议
  3. 03CDS 视图实体模式
  4. 04行为定义与实现示例
  5. 05投影与服务配置建议
  6. 06常见问题与规避方法

适用场景

  1. 01选择托管或非托管 RAP 实现
  2. 02规划 RAP 业务对象结构
  3. 03检查行为定义与处理器
  4. 04避免常见 RAP 实现错误

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/dankromp/skilloverflow --skill abap-rap

安装后在 Claude Code 中输入/abap-rap确认已加载。

附带文件references/quick-reference.mdtemplates/managed-rap-bo.abap

详细介绍

ABAP RAP — 最佳实践

主要参考:https://help.sap.com/docs/abap-cloud/abap-rap BDEF 语法:https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/abenbdl_define_beh.htm 速查表:https://github.com/SAP-samples/abap-cheat-sheets

RAP 架构——三层

RAP 业务对象通过 CDS 数据建模视图、CDS 行为定义以及 ABAP 类中的行为实现来定义。

Database table          (DDIC table — stores active data)
    ↓
CDS view entity         (data model — root + child entities)
    ↓
Behavior Definition     (BDEF — what operations are allowed)
    ↓
Behavior Implementation (ABAP class — business logic)
    ↓
Projection view         (UI-specific subset + aliases)
    ↓
Service Definition      (what to expose)
    ↓
Service Binding         (OData V2 or V4 protocol)

托管式与非托管式——谨慎选择

在托管式 RAP 中,框架会自动处理所有 CRUD 持久化操作,开发者只需专注于业务逻辑。在非托管式 RAP 中,开发者必须手动实现每个保存操作。托管式 RAP 非常适合新开发;在需要集成复杂的现有逻辑或旧系统时,则选择非托管式 RAP。

使用托管式的场景: 从零开始开发、直接访问表、新建 S/4HANA 或 BTP 应用 使用非托管式的场景: 集成现有 BAPI/函数模块、复杂的旧版逻辑、外部持久化


CDS 视图实体——根节点和子节点

" Root CDS view entity
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Travel'
define root view entity ZI_Travel
  as select from ztravel
  composition [0..*] of ZI_TravelItem as _Items
{
  key travel_uuid        as TravelUUID,
      travel_id          as TravelID,
      agency_id          as AgencyID,
      customer_id        as CustomerID,
      begin_date         as BeginDate,
      end_date           as EndDate,
      booking_fee        as BookingFee,
      total_price        as TotalPrice,
      currency_code      as CurrencyCode,
      overall_status     as OverallStatus,
      @Semantics.systemDateTime.createdAt: true
      created_at         as CreatedAt,
      @Semantics.user.createdBy: true
      created_by         as CreatedBy,
      @Semantics.systemDateTime.lastChangedAt: true
      last_changed_at    as LastChangedAt,
      @Semantics.user.lastChangedBy: true
      last_changed_by    as LastChangedBy,
      @Semantics.systemDateTime.localInstanceLastChangedAt: true
      local_last_changed_at as LocalLastChangedAt,
      _Items
}

行为定义——托管式并启用草稿

managed implementation in class ZBP_Travel unique;
strict ( 2 );
with draft;

define behavior for ZI_Travel alias Travel
  persistent table ztravel
  draft table ztravel_d
  etag master LocalLastChangedAt
  lock master total etag LastChangedAt
  authorization master ( global )
{
  field ( readonly ) TravelUUID, TravelID, CreatedAt, CreatedBy, LastChangedAt, LastChangedBy;
  field ( numbering : managed, readonly ) TravelUUID;

  create;
  update;
  delete;

  action ( features : instance ) acceptTravel result [1] $self;
  action ( features : instance ) rejectTravel result [1] $self;

  validation validateDates on save { field BeginDate, EndDate; }
  validation validateStatus on save { field OverallStatus; }

  determination calculateTotalPrice on modify { field BookingFee; }

  draft action Edit;
  draft action Activate optimized;
  draft action Discard;
  draft action Resume;

  association _Items { create; with draft; }

  mapping for ztravel {
    TravelUUID = travel_uuid;
    TravelID   = travel_id;
    AgencyID   = agency_id;
  }
}

行为实现——关键方法

CLASS ZBP_Travel DEFINITION PUBLIC ABSTRACT FINAL FOR BEHAVIOR OF ZI_Travel.
  PUBLIC SECTION.
    " Validation
    METHODS validateDates    FOR VALIDATE ON SAVE
      IMPORTING keys FOR Travel~validateDates.
    " Determination
    METHODS calculateTotalPrice FOR DETERMINE ON MODIFY
      IMPORTING keys FOR Travel~calculateTotalPrice.
    " Action
    METHODS acceptTravel     FOR MODIFY
      IMPORTING keys FOR ACTION Travel~acceptTravel RESULT result.
ENDCLASS.

CLASS ZBP_Travel IMPLEMENTATION.
  METHOD validateDates.
    READ ENTITIES OF ZI_Travel IN LOCAL MODE
      ENTITY Travel
        FIELDS ( BeginDate EndDate )
        WITH CORRESPONDING #( keys )
      RESULT DATA(lt_travel)
      FAILED failed.

    LOOP AT lt_travel INTO DATA(ls_travel).
      IF ls_travel-EndDate < ls_travel-BeginDate.
        APPEND VALUE #(
          %tky = ls_travel-%tky )
          TO failed-travel.
        APPEND VALUE #(
          %tky        = ls_travel-%tky
          %state_area = 'VALIDATE_DATES'
          %msg        = new_message_with_text(
                          severity = if_abap_behv_message=>severity-error
                          text     = 'End date must be after begin date' )
          %element-EndDate = if_abap_behv=>mk-on )
          TO reported-travel.
      ENDIF.
    ENDLOOP.
  ENDMETHOD.

  METHOD calculateTotalPrice.
    " Always use IN LOCAL MODE inside behavior handlers
    MODIFY ENTITIES OF ZI_Travel IN LOCAL MODE
      ENTITY Travel
        UPDATE FIELDS ( TotalPrice )
        WITH VALUE #( FOR key IN keys (
          %tky       = key-%tky
          TotalPrice = '0.00' ) ).
  ENDMETHOD.

  METHOD acceptTravel.
    MODIFY ENTITIES OF ZI_Travel IN LOCAL MODE
      ENTITY Travel
        UPDATE FIELDS ( OverallStatus )
        WITH VALUE #( FOR key IN keys (
          %tky          = key-%tky
          OverallStatus = 'A' ) )
      FAILED   failed
      REPORTED reported.

    READ ENTITIES OF ZI_Travel IN LOCAL MODE
      ENTITY Travel ALL FIELDS WITH CORRESPONDING #( keys )
      RESULT result
      FAILED failed.
  ENDMETHOD.
ENDCLASS.

投影视图 + BDEF

" Projection CDS view (UI-facing)
@EndUserText.label: 'Travel - Projection'
@AccessControl.authorizationCheck: #NOT_REQUIRED
define root view entity ZC_Travel
  provider contract transactional_ui
  as projection on ZI_Travel
{
  key TravelUUID,
      TravelID,
      @UI.lineItem: [{ position: 10 }]
      @UI.selectionField: [{ position: 10 }]
      AgencyID,
      BeginDate,
      EndDate,
      @UI.lineItem: [{ position: 50, criticality: 'OverallStatusCriticality' }]
      OverallStatus,
      _Items : redirected to composition child ZC_TravelItem
}

" Projection BDEF — thin layer, no business logic here
projection;
strict ( 2 );
use draft;

define behavior for ZC_Travel alias Travel
{
  use create;
  use update;
  use delete;
  use action acceptTravel;
  use action rejectTravel;
  use draft action Edit;
  use draft action Activate;
  use draft action Discard;
  use draft action Resume;
  use association _Items { create; with draft; }
}

服务定义 + 绑定

" Service Definition
@EndUserText.label: 'Travel Service'
define service ZUI_Travel_O4 {
  expose ZC_Travel as Travel;
  expose ZC_TravelItem as TravelItem;
  expose /DMO/I_Agency as Agency;
}

在 ADT 中,将服务绑定 ZUI_Travel_O4_VR 激活为 OData V4 UI 绑定。


应避免的常见错误

  • ❌ 在投影 BDEF 或投影视图中添加业务逻辑

  • ✅ 保持投影精简——仅包含 UI 标签、字段别名和 use 语句。接口行为层才是业务逻辑层。

  • ❌ 在处理程序中调用 MODIFY ENTITIES 时未使用 IN LOCAL MODE

  • ✅ 内部修改时始终使用 IN LOCAL MODE,避免权限检查和锁定问题

  • ❌ 跳过草稿处理,打算之后再添加

  • ✅ 从一开始就启用草稿——为现有 RAP BO 补加草稿功能很复杂

  • ❌ 在新开发中使用 strict ( 1 )

  • ✅ 始终使用 strict ( 2 )——启用最全面的 BDEF 语法检查

  • ❌ 在可直接访问表时仍使用非托管式实现 CRUD

  • ✅ 对于从零开始且可直接访问表的开发,使用托管式实现

  • ❌ 忘记在根 BDEF 中定义 etag master 和 lock master

  • ✅ 始终定义 ETag 以实现乐观锁定,并为草稿定义 lock master

  • ❌ 忘记在管理字段上添加 @Semantics.systemDateTime 和 @Semantics.user 注解

  • ✅ 托管式 RAP 需要这些注解才能自动填充时间戳和用户信息

评论

支持 Markdown