基于SAP BTP 创建CAP for Java 示例项目

131 阅读22分钟

基于SAP BTP 创建CAP for Java 示例项目

使用 SAP BTP 试用版

申请使用账号地址

image-20240126183306784

Tips :

image-20240131114550597

如果没有权限 , 按照help.sap.com/docs/bas/sa…进行角色权限

创建 Dev Space

打开 SAP Business Application Studio 首页

image-20240131115414017

在欢迎页面上,选择 Create Dev Space (创建开发空间)

image-20240131115433686

输入 CAPTutorial 作为开发空间的名称,然后选择“全栈云应用程序”作为应用程序类型。继续创建开发人员空间

image-20240131115548140

通过选择全栈云应用程序,你的空间附带了开发 CAP 应用程序所需的多个开箱即用的扩展。例如,内置了 CDS 工具。这样可以节省不必要的设置时间。创建开发空间需要几秒钟。

image-20240131115738208

创建CAP 应用程序框架

转到Terminal → New Terminal.从主菜单中,选择“终端”→“新建终端”

image-20240131115959559

现在应该在窗口底部打开一个终端窗口。在终端中,运行 cd projects 以转到 projects 目录

运行下面命令

mvn -B archetype:generate -DarchetypeArtifactId=cds-services-archetype -DarchetypeGroupId=com.sap.cds \
  -DarchetypeVersion=RELEASE -DjdkVersion=17 \
  -DgroupId=com.sap.cap -DartifactId=products-service -Dpackage=com.sap.cap.productsservice

image-20240131120348880

进入构建好的项目:

image-20240131120427676

  • 该项目名为 products-service
  • db 文件夹存储与数据库相关的项目
  • srv 文件夹存储 Java 应用程序

从主菜单中,选择“文件”→“打开文件夹”。

image-20240131120551466

如果看到一条通知,询问是否要同步 Java classpath/configuration ,请选择 Always (始终)。

如果目前对任何 pom.xml 文件有任何问题指示,请不要担心暂时忽略它们。

定义服务

CAP 应用程序使用 Core Data Services (CDS) 来描述:

右键单击该文件夹,然后选择 “新建文件”。srv 名称 : admin-service.cds

image-20240131120924637

添加内容

service AdminService {
    entity Products {
        key ID : Integer;
        title  : String(111);
        descr  : String(1111);
    }
}

编译

进入终端 输入pwd, 进入项目目录,运行以下命令以触发 maven 构建过程:

mvn clean install

image-20240131121208530

image-20240131121231495

运行

在创建项目框架时,创建了应用程序文件,其中包含一个方法。是 Spring Boot 容器的启动类。

Application.java main Application.java

查看包结构

Application.java

com.sap.cap.productsservice

srv/src/main/java/com/sap/cap/productsservice

如果在 SAP Business Application Studio 中使用 CTRL+P,则会打开一个搜索栏。开始键入以查找并打开文件。Application.java

image-20240131121500180

如你所见,该文件不包含特定于 CAP 的启动说明。这是每个Spring Boot应用程序中的典型样板代码。CAP Java 运行时的初始化由 Spring 根据 .pom.xml

通过在终端中运行以下命令,转到项目的根目录:

cd ~/projects/products-service

通过在终端中运行以下命令来启动应用程序 :

mvn clean spring-boot:run

右下角将出现一条通知消息,指出“服务正在侦听端口 8080”。

image-20240131121639597

点击Open in a New Tab

image-20240131121726942

检查 OData 元数据

从欢迎页面中选择“$metadata”以检查由 CAP Java 运行时自动提供的 OData 元数据。

odata/v4/AdminService/$metadata

image-20240131122306842

添加自定义事件处理程序

为自定义事件处理程序创建 Java 类

创建 Java 包,创建一个名为handlerssrv/src/main/java/com/sap/cap/productsservice下的新文件夹。

创建AdminService.java java类

package com.sap.cap.productsservice.handlers;

import java.util.HashMap;
import java.util.Map;

import org.springframework.stereotype.Component;

import com.sap.cds.services.cds.CdsCreateEventContext;
import com.sap.cds.services.cds.CdsReadEventContext;
import com.sap.cds.services.cds.CqnService;
import com.sap.cds.services.handler.EventHandler;
import com.sap.cds.services.handler.annotations.On;
import com.sap.cds.services.handler.annotations.ServiceName;

@Component
@ServiceName("AdminService")
public class AdminService implements EventHandler {

    private Map<Object, Map<String, Object>> products = new HashMap<>();

    @On(event = CqnService.EVENT_CREATE, entity = "AdminService.Products")
    public void onCreate(CdsCreateEventContext context) {
        context.getCqn().entries().forEach(e -> products.put(e.get("ID"), e));
        context.setResult(context.getCqn().entries());
    }

    @On(event = CqnService.EVENT_READ, entity = "AdminService.Products")
    public void onRead(CdsReadEventContext context) {
        context.setResult(products.values());
    }

}

管理“AdminService”服务中的“Products”实体。它处理创建和读取这些实体的事件,并将数据存储在一个内部映射中。

通过HTTP请求插入数据

尝试将一些数据插入到正在运行的应用程序中。例如,通过使用 SAP Business Application Studio 中捆绑的 HTTP 请求插件

在根目录中创建一个新文件。 requests.http

### Create Product

POST http://localhost:8080/odata/v4/AdminService/Products
Content-Type: application/json

{"ID": 42, "title": "My Tutorial Product", "descr": "You are doing an awesome job!"}

image-20240131123502527

POST 请求会导致对服务的 AdminService 实体 Products 进行 OData 插入。内容的类型在 HTTP 请求的 Content-Type 标头中指定,实际请求的内容以 JSON 形式在请求正文中传递。

选择文件中请求上方的 Send Request。将在窗口右侧看到结果。

image-20240131123619787

访问数据

从cap 欢迎页面访问数据

image-20240131133747166

image-20240131133756237

创建通用的服务

  • 创建一个服务,稍后将在另一个 CAP Java 项目中重用该服务。
  • 使用 CDS 建模时,最佳做法是将服务与模型分开。
  • 转到的 db 文件夹并创建一个名为 schema.cds 的文件

    • image-20240131134125622
    • 将以下代码添加到新创建的 schema.cds 文件,并确保保存文件:

      • namespace sap.capire.products;
        
        using { Currency, cuid, managed, sap.common.CodeList } from '@sap/cds/common';
        
        entity Products : cuid, managed {
            title    : localized String(111);
            descr    : localized String(1111);
            stock    : Integer;
            price    : Decimal(9,2);
            currency : Currency;
            category : Association to Categories;
        }
        
        entity Categories : CodeList {
            key ID   : Integer;
            parent   : Association to Categories;
            children : Composition of many Categories on children.parent = $self;
        }
        

了解关键字

如你所见,域模型定义了两个实体:

  • Products
  • Categories

它还从包(全局可用的重用 @sap/cds/common 包)导入各种通用定义:

  • Currency
  • cuid
  • managed
  • CodeList

此外,域模型还使用 CDS 关键字 localizedAssociationComposition

localized

localized 关键字可用于标记需要翻译的元素。存储不同语言的翻译和存储默认回退翻译的功能由 CDS 自动处理。

Associations and Compositions

Associationscompositions 可用于定义实体之间的关系。它们通常允许你定义这些关系,而无需显式使用外键。

关联(Associations)和组合(Compositions)。让我们用一个简单的例子来理解这两种关系。

想象一下,你在建立一个关于书籍和类别的数据模型。在这个模型中,每本书都属于一个类别,而每个类别可能包含多本书。

关联(Associations) :

  • 关联就像是朋友关系。比如,一本书和它的类别之间的关系就是关联。
  • 在关联中,书和类别是相互独立的。如果你删除了一个类别,它并不会影响到书。这就像是,如果你和你的朋友不再联系,你们各自的生活还是会继续。

组合(Compositions) :

  • 组合则更像是父母和孩子的关系。在你的数据模型中,一个类别可以有子类别,这种父子类别的关系就是组合。
  • 在组合中,子类别依赖于父类别。如果父类别被删除了,它的所有子类别也会被删除。这就像是,如果一个家庭解散了,那么家庭成员之间的关系也就不存在了。

这里类别(Categories)实体定义了一个父子层级关系。这允许你建立一个类别的层级结构。子类别作为组合的一部分被建模。这意味着,当你删除一个父类别时,它的所有子类别也会被自动删除。但是,一个类别的父类别是通过关联来建模的。所以,当你删除一个类别时,它的父类别不会被删除。

简而言之,关联就像是朋友关系,而组合则像是家庭关系。在SAP CAP中,这两种关系让你能够在不直接处理外键的情况下定义实体之间的关系。

aspect : cuid and managed

在SAP CAP中,aspects用于向实体添加额外的元素或特性,而不需要在每个实体中重复相同的代码。这里提到的两个aspects是cuidmanaged

  1. cuid(Client Unique ID) :

    • cuid是一个aspect,它向实体添加了一个名为ID的关键元素。
    • 这个ID元素的类型是UUID(Universally Unique Identifier,通用唯一识别码),这意味着每个实体都会有一个独一无二的标识符。
    • 使用cuid可以确保每个实体都能被唯一地识别,这在数据库中是非常重要的。
  2. managed:

    • managed是另一个aspect,它向实体添加了四个额外的元素
    • 这些元素用于捕捉实体的创建时间、最后更新时间,以及执行创建和最后更新的用户。
    • 具体来说,这四个元素通常是:创建时间(createdAt)、创建者(createdBy)、最后更新时间(modifiedAt)、以及最后更新者(modifiedBy)。
    • 这对于跟踪和维护数据的历史记录非常有用,尤其是在需要审计或了解数据变更历史的情况下。

aspect : CodeList

  • CodeList是一种特殊的aspect,用于存储基于代码的全球性、可翻译的定义。这些定义可以包括货币、国家、语言等。
  • 在用户界面(UI)中,CodeList特别有用,因为它可以为某些输入字段提供值帮助。比如,如果你有一个让用户选择国家的下拉菜单,你可以使用CodeList来填充这个菜单的选项,其中包括所有国家的名称和相应的代码。
  • CodeList的一个关键优点是它支持国际化,这意味着你可以根据用户的语言偏好显示不同语言的值。

Type : Currency

  • Currency是一个定义了货币的类型。
  • 它定义了一个到Currencies实体的关联。这个Currencies实体基于ISO 4217标准,使用三字母的字母代码作为键,例如“EUR”(欧元)或“USD”(美元)。
  • 除了货币代码,Currencies实体还提供存储相应货币符号的可能性,比如“€”(欧元符号)或“$”(美元符号)。
  • 这种类型的设计使得在处理涉及货币的数据时更加方便和标准化,尤其是在需要处理多种货币的国际化应用中。

获取有关 '@sap/cds/common ' 的更多信息

要直接在编辑器中跳转到导入的定义,请按 CTRL ,将鼠标悬停在关键字上,然后单击。这将在单独的编辑器选项卡中打开源文件。

按住 CTRL 关键字并将其悬停在关键字上,然后将手形光标移到关键字上。这会打开一个包含此特定项目定义的微小叠加层。

image-20240131135550066

右键单击(或使用 F12 )定义将打开上下文菜单。例如,可以在那里找到 Peek 定义以获得更大的叠加。不仅显示此特定项的定义,你还可以在不打开文件本身的情况下浏览此定义的整个源文件。

image-20240131135649734

重写AdminService

之前定义了一个名为AdminService 的简单服务,它 直接定义了实体 Products 。由于现在已经在域模型中定义了 Products 实体, AdminService 因此只需要公开它。此外,你还定义了 Categories 实体,该实体也应是服务的一部分。

使用投影(projection) :

  • 投影是一种在服务中暴露实体的方式。通过投影,你可以选择性地包含或排除实体的某些元素,甚至可以重命名元素。
  • 在你的例子中,你将使用最简单的投影形式,即不对领域模型实体进行任何更改地直接暴露它们。

操作步骤:

  • 首先,你需要进入srv文件夹并打开admin-service.cds文件。

  • 然后,用下面的代码替换该文件的内容,并确保保存文件:

    using { sap.capire.products as db } from '../db/schema';
    
    service AdminService {
        entity Products   as projection on db.Products;
        entity Categories as projection on db.Categories;
    }
    
  • 这段代码的意思是,你在AdminService中定义了两个实体:ProductsCategories。这些实体是通过对db(你的数据库模型)中的相应实体进行投影得到的。

理解代码:

  • using { sap.capire.products as db } from '../db/schema';: 这行代码导入了你的数据库模型,这样你就可以在服务中引用它了。
  • entity Products as projection on db.Products;: 这表示Products实体是对数据库模型中Products实体的直接投影。
  • entity Categories as projection on db.Categories;: 同样,这表示Categories实体是对数据库模型中Categories实体的直接投影。

通过这种方式,你的AdminService服务现在将包含ProductsCategories这两个实体,而且是直接反映了你在数据库模型中定义的那样。这使得服务定义变得简洁且易于管理。

使用CAP的通用持久化处理

  1. CAP的通用持久性处理:

    • CAP Java SDK提供了开箱即用的功能,可以从数据库中存储和检索实体。
    • 如果实体存储在数据库中,通常不需要自定义编码。
  2. 自动服务实体:

    • 在你的AdminService中定义的实体将通过OData自动提供服务。
    • 由于CAP处理了大部分工作,你可以删除之前创建的AdminService.java文件。
  3. 操作步骤:

    • 删除handlers文件夹中的AdminService.java文件。
  4. 数据库选择:

    • 默认情况下,CAP Java SDK使用内存中的H2数据库。这意味着当应用程序停止时,数据库中的内容将丢失。
    • 如果你需要在应用程序运行之间持久化数据库,可以使用基于文件的SQLite数据库。这在CAP文档的“使用数据库”部分有详细描述。

运行并测试应用程序

  1. 停止应用程序:

    • 如果你的应用程序仍在运行,首先需要将其停止。
  2. 重新启动应用程序:

    • 在终端中运行命令mvn spring-boot:run来重新启动你的应用程序。
    • 一旦应用程序启动,你可以在新标签页中打开它。
  3. 创建类别(Categories) :

    • 你将通过HTTP请求来创建一些类别。

    • 在你之前创建的

      requests.http
      

      文件中添加以下内容:

      ### Create Categories
      
      POST http://localhost:8080/odata/v4/AdminService/Categories
      Content-Type: application/json
      
      {"ID": 1, "name": "TechEd", "descr": "TechEd related topics", "children": [{"ID": 10, "name": "CAP Java", "descr": "Run on Java"}, {"ID": 11, "name": "CAP Node.js", "descr": "Run on Node.js"}]}
      
    • 然后选择出现在新请求上方的“Send Request”(发送请求)。这个请求将通过深度插入一次性创建多个嵌套类别。

  4. 查询单个类别:

    • 尝试查询单个类别,例如,通过在你的应用程序URL末尾添加以下内容:

      /odata/v4/AdminService/Categories(10)
      
  5. 展开嵌套结构:

    • 你还可以展开嵌套的结构。在应用程序URL的末尾添加以下内容:

      /odata/v4/AdminService/Categories?$expand=children
      /odata/v4/AdminService/Categories(10)?$expand=parent
      /odata/v4/AdminService/Categories(1)?$expand=children
      
  6. 测试完成后停止应用程序:

    • 测试完成后,可以使用CTRL+C停止你的应用程序。

为重用做准备

  1. 修改package.json文件:

    • 首先,打开位于~/projects/products-service文件夹中的package.json文件。

    • name字段的值从products-service-cds更改为@sap/capire-products。这将定义你的重用模块的名称。

    • 如果你愿意,也可以在description字段中提供一个有意义的描述。

      • image-20240131141141006
  2. 添加index.cds文件:

    • 为了更容易重用模块,并确保与其他应用程序的更好解耦,你可以在products-service中添加一个index.cds文件。

    • ~/projects/products-service文件夹中创建一个新的index.cds文件。

    • 将以下内容放入这个文件中,并确保保存文件:

      using from './db/schema';
      using from './srv/admin-service';
      
    • 这个文件将作为模块的入口点,它引用了数据库模式和服务定义。

  3. 完成这些步骤后,你就成功地开发了基于CDS领域模型和服务定义的products-service应用程序,并为将来的重用做好了准备。

创建书店项目

请确保停止了应用程序

新建一个终端 : Terminal New Terminal.

在添加 bookstore 项目之前,我们需要确保位于 projects 文件夹中。两个项目 ( products-servicebookstore ) 应该放在一起。在新创建的终端中运行以下命令以返回到项目文件夹:

cd ~/projects

现在,请运行以下命令:

mvn -B archetype:generate -DarchetypeArtifactId=cds-services-archetype -DarchetypeGroupId=com.sap.cds \
-DarchetypeVersion=RELEASE -DjdkVersion=17 \
-DgroupId=com.sap.cap -DartifactId=bookstore

File Open Folder. 打开bookstore项目

image-20240131141713953

引入服务

product-service作为一个可重用的服务,通过NPM依赖项的形式加入到书店应用程序中。这是SAP CAP(SAP Cloud Application Programming Model)中模块化和重用的一个实际应用示例

  1. 模拟发布product-service模块:

    • 首先,你需要模拟发布product-service模块,并在书店应用程序中使用这个版本。
  2. 打开新的终端:

    • 在SAP Business Application Studio的主菜单中,选择“Terminal → New Terminal”。
    • 在终端中执行以下命令以切换到书店项目的目录:
    • cd ~/projects/bookstore
  3. 安装可重用服务项目作为NPM依赖项:

    • 执行以下命令来安装product-service作为依赖项:

      npm install $(npm pack ../products-service -s)
      
    • npm pack命令会从products-service创建一个tarball(压缩包文件),然后直接将其用作书店应用程序中的依赖项。

  4. 查找压缩包文件:

    • 在书店项目的根文件夹中,你将找到一个名为sap-capire-products-1.0.0.tgz的文件,这是products-service项目的tarball文件。
  5. 安装其他包并简化依赖结构:

    • 执行以下命令来安装所有其他包并简化整体依赖结构:
    • npm install && npm dedupe
      
  6. 检查package.json文件:

    • 打开你的书店项目的package.json文件,你将看到对@sap/capire-products的依赖。
    • image-20240131143017493

定义书店域模型

  1. 创建schema.cds文件:

    • ~/projects/bookstore/db文件夹中,创建一个名为schema.cds的文件。
  2. 添加代码到schema.cds文件:

    • 将以下代码添加到你刚创建的schema.cds文件中,并确保保存文件:
    • namespace sap.capire.bookstore;
      
      using { Currency, cuid, managed }      from '@sap/cds/common';
      using { sap.capire.products.Products } from '@sap/capire-products';
      
      entity Books as projection on Products; extend Products with {
          // Note: we map Books to Products to allow reusing AdminService as is
          author : Association to Authors;
      }
      
      entity Authors : cuid {
          firstname : String(111);
          lastname  : String(111);
          books     : Association to many Books on books.author = $self;
      }
      
      @Capabilities.Updatable: false
      entity Orders : cuid, managed {
          items    : Composition of many OrderItems on items.parent = $self;
          total    : Decimal(9,2) @readonly;
          currency : Currency;
      }
      
      @Capabilities.Updatable: false
      entity OrderItems : cuid {
          parent    : Association to Orders not null;
          book_ID   : UUID;
          amount    : Integer;
          netAmount : Decimal(9,2) @readonly;
      }
      
    • 这个领域模型定义了四个实体:BooksAuthorsOrdersOrderItems
  3. 理解代码:

    • 导入了Currencycuidmanaged类型和aspects,这些在之前的教程中有描述。
    • 导入了Products实体,并将其重用为Books实体。为了建立书籍和作者之间的关系,Products实体被扩展了一个额外的关联到Authors
    • Orders实体的total元素和OrderItems实体的netAmount元素被标注为@readonly,意味着这些元素的值不能由客户端设置,而是通过自定义代码计算。这部分自定义代码将在后续教程中实现。
    • OrdersOrderItems实体都被标注为@Capabilities.Updatable: false,意味着它们不能被更新,只能被创建和删除。

定义书店服务

  1. 创建services.cds文件:

    • ~/projects/bookstore/srv文件夹中,创建一个名为services.cds的文件。
  2. 添加代码到services.cds文件:

    • 将以下代码添加到services.cds文件中,并确保保存文件:

      using { sap.capire.bookstore as db } from '../db/schema';
      
      // Define Books Service
      service BooksService {
          @readonly entity Books as projection   on db.Books { *, category as genre } excluding { category, createdBy, createdAt, modifiedBy, modifiedAt };
          @readonly entity Authors as projection on db.Authors;
      }
      
      // Define Orders Service
      service OrdersService {
          entity Orders as projection on db.Orders;
          entity OrderItems as projection on db.OrderItems;
      }
      
      // Reuse Admin Service
      using { AdminService } from '@sap/capire-products';
      extend service AdminService with {
          entity Authors as projection on db.Authors;
      }
      
    • 这个services.cds文件定义了三个服务:BooksServiceOrdersServiceAdminService

  3. 理解代码:

    • BooksService用于提供对BooksAuthors数据的只读视图。通过这个服务无法修改这些实体。
    • OrdersService允许查看、创建和删除订单。
    • AdminService是从产品服务中重用的,但我们向其中添加了Authors实体。它可以用于创建、更新和删除产品和作者。
  4. 服务的最佳实践:

    • 通常最好根据单一用例来定义服务。例如,AdminService用于管理产品、作者和类别,而BooksService用于展示书籍和作者的目录,对最终用户隐藏了诸如创建和修改时间等管理数据。

加载示例数据

  1. 创建数据文件夹:

    • 在你的书店项目中,右键点击db文件夹并选择“新建文件夹”。将这个新文件夹命名为data
  2. 进入数据文件夹:

    • 在终端中运行以下命令以进入data文件夹:

      cd ~/projects/bookstore/db/data
      
  3. 下载CSV数据文件:

    • Authors实体下载CSV数据文件:

      curl https://raw.githubusercontent.com/SAP-samples/cloud-cap-samples/CAA160-final/bookstore/db/data/sap.capire.bookstore-Authors.csv -O
      
    • Books实体下载CSV数据文件:

      curl https://raw.githubusercontent.com/SAP-samples/cloud-cap-samples/CAA160-final/bookstore/db/data/sap.capire.bookstore-Books.csv -O
      
    • 将下载的Books CSV文件重命名:

      mv sap.capire.bookstore-Books.csv sap.capire.products-Products.csv
      
    • 下载Books实体的翻译CSV数据文件:

      curl https://raw.githubusercontent.com/SAP-samples/cloud-cap-samples/CAA160-final/bookstore/db/data/sap.capire.bookstore-Books_texts.csv -O
      
    • 将下载的Books_texts CSV文件重命名:

      mv sap.capire.bookstore-Books_texts.csv sap.capire.products-Products_texts.csv
      
    • Categories实体下载CSV数据文件:

      curl https://raw.githubusercontent.com/SAP-samples/cloud-cap-samples/CAA160-final/bookstore/db/data/sap.capire.products-Categories.csv -O
      
  4. 确认CSV文件:

    • 确保你现在有4个CSV文件,包含示例数据。这些文件为我们重用的服务和在书店服务中创建的一个实体提供初始数据:

      • sap.capire.products-Categories.csv
      • sap.capire.products-Products.csv
      • sap.capire.products-Products_texts.csv
      • sap.capire.bookstore-Authors.csv
  5. 注意CSV文件命名:

    • CSV文件的命名必须精确匹配模式[namespace]-[entity name],否则应用程序将无法启动。

运行和测试你的书店应用程序

  1. 进入书店项目的根目录:

    • 在终端中运行以下命令以进入书店项目的根目录:

      cd ~/projects/bookstore
      
  2. 停止之前运行的应用程序:

    • 确保你已经使用CTRL+C停止了所有之前运行的应用程序(包括products-service应用程序)。
  3. 启动应用程序:

    • 通过运行以下命令来启动应用程序:

      mvn spring-boot:run
      
    • 在SAP Business Application Studio中,你将看到一个弹出窗口。选择“在新标签页中打开”。

  4. 打开URL并查看数据:

    • 当你在新标签页中打开URL时,你将看到一个欢迎页面。要查看书籍数据,可以直接从欢迎页面上点击“Books”。
    • 作为另一种方式,你也可以在URL后面直接添加/odata/v4/BooksService/Books来访问书籍数据。
    • image-20240131144107019
  5. 阅读本地化的德语示例数据:

    • 要阅读本地化的德语示例数据,请在URL中添加查询参数sap-locale=de。例如,<APP_URL>/odata/v4/BooksService/Books?sap-locale=de。尝试在德语(de)和英语(en)之间切换语言。image-20240131144329184

使用自定义代码扩展书店

在CAP Java应用程序中,你可以使用@Before@After注解来增强默认的事件处理。@Before注解用于在事件发生之前执行操作,例如验证输入数据,而@After注解用于在事件发生之后执行操作,例如处理返回的实体。以下是你需要执行的步骤:

  1. 停止应用程序:

    • 如果你的应用程序仍在运行,请使用CTRL+C停止它。
  2. 创建handlers文件夹:

    • 在终端中,进入srv/src/main/java/com/sap/cap/bookstore目录。
    • 创建一个新的文件夹,命名为handlers
  3. 创建OrdersService.java文件:

    • handlers包中,创建一个名为OrdersService.java的文件。

    • 将以下内容添加到OrdersService.java文件中,并确保保存文件:

      package com.sap.cap.bookstore.handlers;
      
      import cds.gen.ordersservice.OrdersService_;
      import com.sap.cds.services.handler.EventHandler;
      import com.sap.cds.services.handler.annotations.ServiceName;
      
      import org.springframework.stereotype.Component;
      
      @Component
      @ServiceName(OrdersService_.CDS_NAME)
      public class OrdersService implements EventHandler {
          // Replace this comment with the code of Step 2 of this tutorial
      }
      
    • 这个类将作为OrdersService的事件处理器。

  4. 解决编辑器中的验证错误:

    • 如果你在编辑器中看到验证错误,请在srv目录中的pom.xml上打开上下文菜单并选择“重新加载项目”。
    • 这将重新生成类并使它们可用。

创建订单时减少库存

  1. 添加方法到OrdersService Java类:

    • 将以下代码添加到你的OrdersService Java类中,并确保保存文件:
    • @Autowired
      PersistenceService db;
      
      @Before(event = CqnService.EVENT_CREATE, entity = OrderItems_.CDS_NAME)
      public void validateBookAndDecreaseStock(List<OrderItems> items) {
          for (OrderItems item : items) {
              String bookId = item.getBookId();
              Integer amount = item.getAmount();
      
              // check if the book that should be ordered is existing
              CqnSelect sel = Select.from(Books_.class).columns(b -> b.stock()).where(b -> b.ID().eq(bookId));
              Books book = db.run(sel).first(Books.class)
                      .orElseThrow(() -> new ServiceException(ErrorStatuses.NOT_FOUND, "Book does not exist"));
      
              // check if order could be fulfilled
              int stock = book.getStock();
              if (stock < amount) {
                  throw new ServiceException(ErrorStatuses.BAD_REQUEST, "Not enough books on stock");
              }
      
              // update the book with the new stock, means minus the order amount
              book.setStock(stock - amount);
              CqnUpdate update = Update.entity(Books_.class).data(book).where(b -> b.ID().eq(bookId));
              db.run(update);
          }
      }
      
      @Before(event = CqnService.EVENT_CREATE, entity = Orders_.CDS_NAME)
      public void validateBookAndDecreaseStockViaOrders(List<Orders> orders) {
          for (Orders order : orders) {
              if (order.getItems() != null) {
                  validateBookAndDecreaseStock(order.getItems());
              }
          }
      }
      
  2. 添加导入语句:

    • OrdersService Java类的顶部添加以下导入语句,并确保保存文件:
    • import java.util.List;
      import org.springframework.beans.factory.annotation.Autowired;
      
      import com.sap.cds.ql.Select;
      import com.sap.cds.ql.Update;
      import com.sap.cds.ql.cqn.CqnSelect;
      import com.sap.cds.ql.cqn.CqnUpdate;
      import com.sap.cds.services.ErrorStatuses;
      import com.sap.cds.services.ServiceException;
      import com.sap.cds.services.cds.CqnService;
      import com.sap.cds.services.handler.annotations.Before;
      import com.sap.cds.services.persistence.PersistenceService;
      
      import cds.gen.ordersservice.OrderItems;
      import cds.gen.ordersservice.OrderItems_;
      import cds.gen.ordersservice.Orders;
      import cds.gen.ordersservice.Orders_;
      import cds.gen.sap.capire.bookstore.Books;
      import cds.gen.sap.capire.bookstore.Books_;
      
  3. 理解代码逻辑:

    • validateBookAndDecreaseStock方法使用@Before注解注册,这意味着该方法在OrderItems实体持久化之前被调用。该注解还指定了在创建OrderItems实体时调用该方法。
    • 方法检查要订购的书籍是否存在,并比较可用库存与订购数量。如果库存足够,则减少书籍的库存并在数据库中更新书籍。
    • validateBookAndDecreaseStockViaOrders方法用于处理通过Orders实体的深度插入创建的订单项。
  4. 处理可能的错误:

    • 如果OrdersService.java文件仍然显示错误,请在srv目录中右键单击pom.xml并选择“重新加载项目”。
    • 关闭并重新打开OrderService.java文件后,错误应该消失。

测试处理程序

  1. 停止应用程序:

    • 在SAP Business Application Studio的终端中,如果应用程序仍在运行,请使用CTRL+C停止它。
  2. 设置运行配置:

    • 在SAP Business Application Studio的侧边栏中,选择运行配置图标。
    • 选择创建配置图标(加号),然后选择Bookstore作为你要运行的项目。按Enter确认名称。
    • image-20240131150320910
  3. 启动应用程序:

    • 点击绿色箭头来启动应用程序。
    • 你应该在调试控制台中看到应用程序正在启动。
  4. 测试应用程序:

    • 在根目录中创建一个新的文件requests.http

    • 在文件中输入以下内容:

      ### Create Order
      
      POST http://localhost:8080/odata/v4/OrdersService/Orders
      Content-Type: application/json
      
      {
        "items": [
          {
            "book_ID": "abed2f7a-c50e-4bc5-89fd-9a00a54b4b16",
            "amount": 2
          }
        ]
      }
      
    • 选择“发送请求”来执行请求。

  5. 查看库存变化:

    • 从欢迎页面上,选择Books,你将看到书籍《Wuthering Heights》的库存减少到10。
    • 你也可以在应用程序URL的末尾添加/odata/v4/BooksService/Books来查看。
    • 记住应用程序URL是在你运行应用程序时创建的。
  6. 添加并执行第二个请求:

    • 在你创建的requests.http文件中添加第二个请求:

      ### Read Book
      
      GET http://localhost:8080/odata/v4/BooksService/Books(abed2f7a-c50e-4bc5-89fd-9a00a54b4b16)
      Accept: application/json
      
    • 选择第二个请求上方的“发送请求”来执行请求。你将看到你正在订购的书籍的当前库存。

  7. 重复请求直到库存耗尽:

    • 重复请求,直到你得到一个错误,表明书籍已经缺货。
    • 通过重复请求,你每次都在订购2本书,因此每次都会减少2本书的库存。
    • image-20240131150700943

计算订单项的netAmount

  1. 添加计算净金额的方法:

    • 将以下代码添加到OrdersService类中,并确保保存文件:

      @After(event = { CqnService.EVENT_READ, CqnService.EVENT_CREATE }, entity = OrderItems_.CDS_NAME)
      public void calculateNetAmount(List<OrderItems> items) {
          for (OrderItems item : items) {
              String bookId = item.getBookId();
      
              // get the book that was ordered
              CqnSelect sel = Select.from(Books_.class).where(b -> b.ID().eq(bookId));
              Books book = db.run(sel).single(Books.class);
      
              // calculate and set net amount
              item.setNetAmount(book.getPrice().multiply(new BigDecimal(item.getAmount())));
          }
      }
      
  2. 添加导入语句:

    • OrdersService Java类的顶部添加以下导入语句,并保存文件:

      import java.math.BigDecimal;
      import com.sap.cds.services.handler.annotations.After;
      
  3. 理解代码逻辑:

    • calculateNetAmount方法使用@After注解注册,这意味着该方法在从数据库读取OrderItems实体后被调用。该注解还指定了在读取或创建OrderItems实体时调用该方法。
    • 方法的items参数提供了对所有读取或创建的OrderItems实体的访问。
    • CqnSelect sel变量定义了一个数据库查询,用于检索订单项引用的书籍。执行查询后,使用Books的POJO接口访问查询结果。
    • 在最后一行中,根据书籍的价格和订购的数量计算订单项的净金额。

再次测试

在SAP Business Application Studio中,如果应用程序仍在运行,请单击“调试”侧面板中的红色停止图标,停止应用程序。

image-20240131150957145

选择SAP Business Application Studio侧面板上的Run Configuration 图标。

image-20240131151035502

现在,向requests.http文件请求添加新请求。

### Create another Order

POST http://localhost:8080/odata/v4/OrdersService/Orders
Content-Type: application/json

{
  "items": [
    {
      "book_ID": "fd0c5fda-8811-4e20-bcff-3a776abc290a",
      "amount": 4
    }
  ]
}

send Request 发送请求

在欢迎页面中,选择OrderItems,你将看到netAmount元素中填充了计算出的值。

image-20240131151305863

image-20240131151316047

计算订单总额

  1. 添加计算总金额的方法:

    • 将以下代码添加到OrdersService类中,并确保保存文件:

      • @After(event = { CqnService.EVENT_READ, CqnService.EVENT_CREATE }, entity = Orders_.CDS_NAME)
        public void calculateTotal(List<Orders> orders) {
            for (Orders order : orders) {
                // calculate net amount for expanded items
                if(order.getItems() != null) {
                    calculateNetAmount(order.getItems());
                }
        
                // get all items of the order
                CqnSelect selItems = Select.from(OrderItems_.class).where(i -> i.parent().ID().eq(order.getId()));
                List<OrderItems> allItems = db.run(selItems).listOf(OrderItems.class);
        
                // calculate net amount of all items
                calculateNetAmount(allItems);
        
                // calculate and set the orders total
                BigDecimal total = new BigDecimal(0);
                for(OrderItems item : allItems) {
                    total = total.add(item.getNetAmount());
                }
                order.setTotal(total);
            }
        }
        
  2. 理解代码逻辑:

    • calculateTotal方法使用@After注解注册,这意味着该方法在从数据库读取Orders实体后被调用。该注解还指定了在读取或创建Orders实体时调用该方法。
    • 对于作为操作一部分可能返回的订单项,使用calculateNetAmount方法计算净金额。请注意,这可能只是订单所有项的一部分。
    • 对于每个订单,使用在CqnSelect selItems中定义的查询从数据库中读取所有订单项。
    • 对于每个订单项,首先通过重用calculateNetAmount方法计算净金额。然后将所有净金额加到订单的总金额中。

测试订单总额

在SAP Business Application Studio中,如果应用程序仍在运行,请单击“调试”侧面板中的红色停止图标,停止应用程序。

选择SAP Business Application Studio侧面板上的Run Configuration 图标。运行书店项目

  1. requests.http 文件的第三个请求中将 amount 更新为 10

  2. 选择第三个请求上方的 Send Request (发送请求)

  3. 在欢迎页面中,选择 Orders。你将看到元素 total 填充了计算值。

    1. image-20240131151935258
  4. URL 末尾加入?$expand=items

    1. image-20240131152022638

添加身份验证和授权

启用身份验证

编辑srv 目录 pom.xml文件 (而不是位于根项目文件夹中的 pom.xml 文件)添加 cds-starter-cloudfoundry 依赖项。并保存文件。

<dependency>
    <groupId>com.sap.cds</groupId>
    <artifactId>cds-starter-cloudfoundry</artifactId>
</dependency>

向订单服务添加身份验证

目前,你只需要确保想要创建订单的用户已经通过身份验证。CAP为本地开发提供了内置的模拟用户,代表常见的身份验证场景。

  1. 启动应用程序:

    • 使用命令mvn spring-boot:run启动应用程序。
  2. 尝试未经认证的请求:

    • 打开requests.http文件,并执行第一个“创建订单”的请求,方法是选择请求上方的“发送请求”。

    • 观察响应是否包含状态HTTP/1.1 401。这表明请求由于未经认证而被拒绝。

      • image-20240131152754870
  3. 修改请求以包含认证信息:

    • 为了创建订单,你需要提供凭据。对于本地开发,CAP提供了内置的模拟用户。修改请求如下:

      ### Create Order
      
      POST http://localhost:8080/odata/v4/OrdersService/Orders
      Content-Type: application/json
      Authorization: Basic authenticated:
      
      {
        "items": [
          {
            "book_ID": "abed2f7a-c50e-4bc5-89fd-9a00a54b4b16",
            "amount": 2
          }
        ]
      }
      
    • 在这里,你已经向请求中添加了Authorization HTTP头,并为内置的已认证模拟用户提供了凭据。这个用户的密码为空。

  4. 再次执行请求并验证结果:

    • 再次执行请求,并查看订单是否现在被创建。
    • image-20240131152823328
    • 当在欢迎页面上选择OrdersService的实体时,你现在也需要提供凭据。在那里,你也可以使用已认证用户和空密码。

将模拟用户添加到项目

  1. 添加安全配置:

    • srv/src/main/resources下的application.yaml文件中添加安全配置部分:

      ---
      spring:
        config.activate.on-profile: default
      cds:
        datasource:
          auto-config.enabled: false
        security:
          mock:
            users:
              - name: klaus
                password: pass_klaus
                additional:
                  firstName: Klaus
                  lastName: Sussard
                  email: Klaus.Sussard@mail.com
              - name: mia
                password: pass_mia
                additional:
                  firstName: Mia
                  lastName: Bonnellac
                  email: Mia.Bonnellac@mail.com
      
    • 这里你定义了两个用户,它们没有明确的角色分配,将隐式地属于authenticated-user伪角色。

  2. 重启应用程序:

    • 使用命令mvn spring-boot:run重启应用程序。
    • 在启动日志中,你可以观察到创建的模拟用户及其用户名、角色和密码。这些用户是除了内置模拟用户之外添加的。
  3. 修改HTTP请求以包含模拟用户的凭据:

    • 修改你之前使用的HTTP请求,以包含其中一个模拟用户的凭据:

      ### Create Order
      
      POST http://localhost:8080/odata/v4/OrdersService/Orders
      Content-Type: application/json
      Authorization: Basic klaus:pass_klaus
      
      {
        "items": [
          {
            "book_ID": "abed2f7a-c50e-4bc5-89fd-9a00a54b4b16",
            "amount": 2
          }
        ]
      }
      
    • 选择请求上方的“发送请求”,查看是否创建了新订单。

  4. 观察响应内容:

    • 响应的有效载荷应包含在createdBymodifiedBy字段中的用户名称,这是由你之前在领域模型中添加的managed方面提供的。
    • image-20240131153450564

将用户角色添加到项目

添加一个具有“Administrators”角色的模拟用户,并限制只有具有该角色的用户才能访问AdminService。以下是你需要执行的步骤:

  1. 添加具有角色的模拟用户:

    • application.yaml文件中,在现有用户后添加一个新的模拟用户,如下所示:

      ---
      spring:
        config.activate.on-profile: default
      cds:
        datasource:
          auto-config.enabled: false
        security:
          mock:
            users:
              - name: klaus
                password: pass_klaus
                additional:
                  firstName: Klaus
                  lastName: Sussard
                  email: Klaus.Sussard@mail.com
              - name: mia
                password: pass_mia
                additional:
                  firstName: Mia
                  lastName: Bonnellac
                  email: Mia.Bonnellac@mail.com
              - name: sabine
                password: pass_sabine
                roles:
                  - Administrators
                additional:
                  firstName: Sabine
                  lastName: Autumnpike
                  email: Sabine.Autumnpike@mail.com
      
    • 使用roles属性为该用户添加“Administrators”角色。

  2. 限制AdminService的访问:

    • srv目录中的services.cds文件末尾添加注释定义,以使AdminService仅对具有“Administrators”角色的用户可用:

      annotate AdminService @(requires: 'Administrators');
      
  3. 重启应用程序:

    • 使用命令mvn spring-boot:run重启应用程序。
  4. 添加新的HTTP请求:

    • requests.http文件中添加一个新的请求:

      ### Read Products
      
      GET http://localhost:8080/odata/v4/AdminService/Products
      Accept: application/json
      Authorization: Basic sabine:pass_sabine
      
    • 选择这个请求上方的“发送请求”,查看是否收到产品列表。

  5. 测试访问控制:

    • 移除Authorization头或将凭据更改为不同的模拟用户。
    • 观察AdminService是否对他们不可用。

    image-20240131154155335

实体的高级授权

实现以下用例:

  • 每个经过身份验证的用户只能查看他们自己的订单和订单项。
  • 管理员应该能够查看所有用户的所有订单。

你可以使用@restrict注解为你的服务添加更复杂的授权检查。

  1. 修改OrdersService的服务定义:

    • srv文件夹中的services.cds文件中,修改OrdersService的服务定义如下:

      // Define Orders Service
      service OrdersService {
          @(restrict: [
              { grant: '*', to: 'Administrators' },
              { grant: '*', where: 'createdBy = $user' }
          ])
          entity Orders as projection on db.Orders;
      
          @(restrict: [
              { grant: '*', to: 'Administrators' },
              { grant: '*', where: 'parent.createdBy = $user' }
          ])
          entity OrderItems as projection on db.OrderItems;
      }
      
    • 这样,你为管理员授予了访问所有订单的权限,而普通用户只能看到他们自己创建的订单。由于你将OrderItems作为一个单独的实体暴露,你也需要在那里添加安全配置。

  2. 重启应用程序:

    • 使用命令mvn spring-boot:run重启应用程序。
  3. 执行HTTP请求以创建订单:

    • 使用你之前添加的模拟用户的凭据执行HTTP请求来创建订单:

      ### Create Order as Mia
      
      POST http://localhost:8080/odata/v4/OrdersService/Orders
      Content-Type: application/json
      Authorization: Basic mia:pass_mia
      
      {
        "items": [
          {
            "book_ID": "fd0c5fda-8811-4e20-bcff-3a776abc290a",
            "amount": 10
          }
        ]
      }
      
  4. 验证不同用户的访问权限:

    • 通过添加以下请求到requests.http文件来验证每个用户(除了管理员)只能访问他们自己的订单和项:

      ### Read Orders as Mia
      
      GET http://localhost:8080/odata/v4/OrdersService/Orders?$expand=items
      Accept: application/json
      Authorization: Basic mia:pass_mia
      
    • 你将看到自己的订单和项。

      ### Read Orders as Klaus
      
      GET http://localhost:8080/odata/v4/OrdersService/OrderItems
      Accept: application/json
      Authorization: Basic klaus:pass_klaus
      
    • 你将不会看到任何项。

      ### Read Orders as Sabine (Administrator)
      
      GET http://localhost:8080/odata/v4/OrdersService/Orders?$expand=items
      Accept: application/json
      Authorization: Basic sabine:pass_sabine
      
    • 你将看到所有订单和项。

将 CAP Java 应用部署到 SAP Business Technology Platform

在SAP BTP驾驶舱中使用SAP HANA Cloud试用版

在 SAP BTP Cockpit 中,点击你的子帐户。

image-20240131155310114

然后单击左侧菜单中的“权利”,搜索 SAP HANA 的权利。

image-20240131155400165

请注意有以下权利 :

  • SAP HANA Cloud: tools (Application), hana, hana-cloud-connection, and relational-data-lake
  • SAP HANA Schemas & HDI Containers: hdi-shared, schema, and securestore

添加对SAP HANA Cloud工具的订阅

  1. 从SAP BTP驾驶舱中,单击服务,然后单击服务市场。搜索SAP HANA云,然后单击右上角的创建。

image-20240131155642552

  1. 在服务下选择SAP HANA Cloud,在计划下选择 tools。 然后点击 Create
  1. 要确保你所需的用户具有在HANA Cloud Central中管理实例的必要权限,请导航到左侧菜单中的“安全”>“用户”。然后单击你的用户。

image-20240131155906412

单击“Assign Role Collection ”按钮。

选择SAP HANA Cloud Administrator,然后单击分配角色集合。

导航到“实例”、“实例”和“订阅”,然后单击SAP HANA Cloud以打开SAP HANA云中心。

image-20240131160314774

image-20240131162713692

创建一个Instanse

bookstore-db

image-20240131163011556

增强生产项目配置

切换到项目的根目录执行:

cds add hana,mta,xsuaa,approuter --for production
  • 这个命令会执行以下操作:

    • hana: 为SAP HANA数据库配置部署,将数据源类型为hana的配置添加到requires.[production].db块中。
    • mta: 添加mta.yaml文件,该文件反映了你的项目配置。
    • xsuaa: 创建xs-security.json文件,并在mta.yaml文件中添加所需配置。在requires.[production].auth块中添加xsuaa类型的认证。
    • approuter: 添加独立AppRouter的配置和所需文件,以确保部署后身份验证流程正常工作。
  • 确保cds-starter-cloudfoundry依赖项包含在你的项目中

    • <dependency>
          <groupId>com.sap.cds</groupId>
          <artifactId>cds-starter-cloudfoundry</artifactId>
      </dependency>
      

更新xs-security.jsonxs-app.json

  1. 更新xs-security.json文件:

    • 打开SAP Business Application Studio中的xs-security.json文件。

    • 更新文件内容,以便它看起来像这样:

      {
        "xsappname": "bookstore",
        "tenant-mode": "dedicated",
        "scopes": [
          {
            "name": "$XSAPPNAME.Administrators",
            "description": "Administrators"
          }
        ],
        "attributes": [],
        "role-templates": [
          {
            "name": "Administrators",
            "description": "generated",
            "scope-references": [
              "$XSAPPNAME.Administrators"
            ],
            "attribute-references": []
          }
        ],
        "role-collections": [
          {
            "name": "BookStore_Administrators",
            "description": "BookStore Administrators",
            "role-template-references": ["$XSAPPNAME.Administrators"]
          }
        ],
        "oauth2-configuration": {
          "redirect-uris": ["https://*.cfapps.us10-001.hana.ondemand.com/**"]
        }
      }
      
    • xsappname属性中添加你的应用程序名称,并声明一个角色集合,以便你稍后可以将用户分配给它。

  2. 根据部署环境更新OAuth2配置:

    • oauth2-configuration的值取决于你的账户部署的环境。
    • 检查命令cf target返回的API URL,并相应地更改值中的数据中心ID,例如https://*.cfapps.us10-001.hana.ondemand.com/**
  3. 更新xs-app.json文件:

    • 打开app/xs-app.json文件。
    • 移除welcomeFile属性。

查看SAP BTP Cloud Foundry地址

访问cockpit.hanatrial.ondemand.com/cockpit#/ho…

image-20240131164039659

image-20240131164058012

复制Cloud Foundry API

image-20240131164236031

登录SAP BTP Cloud Foundry环境

  1. 在SAP Business Application Studio中,选择“终端”打开终端→ 从主菜单中新建终端。

  2. 运行以下命令以配置要在终端中连接到的Cloud Foundry环境。将<CF_API_ENDPOINT>替换为在上一步骤中获得的实际值

    1. cf api <CF_API_ENDPOINT>
      
  3. 在终端中使用以下命令,使用登录凭据进行身份验证:

    1. cf login
      

      image-20240131164538758

使用cf Deploy进行部署

确保dev空间中有一个hana示例

image-20240131182446022

  1. 构建部署档案:

    • 在项目的根目录中,执行以下命令来构建部署档案:

      mbt build -t gen --mtar mta.mtar
      
    • 这需要MBT构建工具,SAP Business Application Studio已经安装了这个工具。

    • -t选项定义构建结果的目标文件夹为项目的gen文件夹。作为这个构建的一部分,隐式地执行了cds build --production。这个隐式构建然后使用了你在使用--for production时添加的所有配置。

  2. 部署档案:

    • 使用cf deploy命令部署档案:

      cf deploy gen/mta.mtar
      
    • 这需要MultiApps CF CLI插件,SAP Business Application Studio已经安装了这个插件。

    • 在部署过程中,将创建所有需要的服务实例,并部署应用程序以及数据库工件。

  3. 观察部署过程:

    • 这个过程可能需要几分钟。在这一步中,档案被上传到Cloud Foundry,服务实例被创建,应用程序被准备,并部署到它们的目标运行时。
    • 如果部署在部署bookstore-db-deployer时失败,请确保你的IP地址已配置为允许连接到SAP HANA Cloud。或者,你可以自行冒险允许所有IP地址的连接。我们建议在完成教程后恢复该设置。
  4. 找到应用程序的URL:

    • 在部署日志中,找到bookstore应用程序的URL:

      Application "bookstore" started and available at "[org]-[space]-bookstore.cfapps.[region].hana.ondemand.com"
      
    • 这是AppRouter的URL,它强制执行身份验证流程。

  5. 在浏览器中打开应用程序:

    • 在浏览器中打开这个URL,并尝试提供的链接,例如.../catalog/Books。应用程序数据从SAP HANA中获取。
    • 相应的路由可以在上一步的routes下找到。
    • image-20240131183204882
  6. 观察应用程序的安全性:

    • 观察你的应用程序现在通过要求在服务和实体端点上进行身份验证来确保安全性。

在SAP BTP上配置身份验证和授权

配置在SAP BTP上进行测试的角色

在浏览器中打开应用程序。使用欢迎页面上的链接,你可以检查是否无法访问 Orders 实体或 AdminService 下的所有内容。如果你单击这些,你应该会看到一个 401 错误。

若要使用 AdminService ,需要将自己分配给 xs-security.json 文件中定义的角色集合 BookStore_Administrators 。若要将此角色集合分配给用户,需要导航到 SAP BTP 子帐户的“安全→角色集合”部分

image-20240131183710213

然后选择 Edit。 在 ID 和 E-Mail 字段中输入你的电子邮件地址,然后选择 Save (保存)

若要使对角色集合的更改生效,需要重新启动 approuter:

cf restart bookstore

再次访问,应该能访问到数据

参考

BTP 权限配置