聊聊 SAP 产品 UI 上的消息显示机制

这是 Jerry 2021 年的第 60 篇文章,也是汪子熙公众号总共第 337 篇原创文章。

本文从 StackOverflow 社区上来自 Partner 的一个 SAP Commerce Cloud Spartacus UI 的定制化需求说起。

聊聊 SAP 产品 UI 上的消息显示机制_第1张图片

这个需求的背景是,客户在 SAP 电商云的产品明细页面,可以留下自己的评论,点击 Submit 按钮提交。

聊聊 SAP 产品 UI 上的消息显示机制_第2张图片

提交之后,能看到“谢谢评论”的提示消息。
聊聊 SAP 产品 UI 上的消息显示机制_第3张图片

客户定制化需求是:不执行这个默认的消息显示逻辑,即不显示消息,而是执行其他逻辑,比如短信通知或邮件通知。

我们先简单回顾 SAP 其他产品的 UI 消息显示机制。

消息是应用程序执行过程中给用户提供反馈的重要渠道之一,通常由用户某个动作直接触发,显示在产品界面上。当然应用程序后台作业运行到某个阶段,在满足指定条件时也能触发消息显示。简明、清晰而准确的消息,能帮助用户明确程序当前的运行状况,指引其下一步的操作。

SAP 产品 UI 显示的消息文本,均有专业的 Knowledge Management 即 KM 团队负责审查和发布。

对于 SAP 开发人员来说,更关心的则是这些消息显示的上下文;换言之,看到 UI 上显示一条消息之后,能否在最短的时间内,高效定位到抛出该消息的准确代码位置。

如下图所示,在经典的 SAPGUI SE38 ABAP 程序编辑器,输入一个不存在的报表名称,会显示一条消息:

Program XX does not exist.

其中 XX 是占位符,会被用户实际输入的报表名称所替换。

聊聊 SAP 产品 UI 上的消息显示机制_第4张图片

基于 ABAP 实现的所有 SAP 产品,比如 SAP CRM,SAP SRM,SAP S/4HANA,SAP Cloud for Customer,UI 上显示的每一条消息,在 ABAP 后台均有一条对应的消息记录,维护在事物码 SE91 里。

以上图的消息为例,其号码为 DS017,其中 DS 为消息类别, 017 为消息编号,二者唯一确认一条消息记录。

聊聊 SAP 产品 UI 上的消息显示机制_第5张图片

Jerry 之前的文章SAP 错误消息调试之七种武器:让所有的错误消息都能被定位,曾经介绍过七种不同的方法,均能在 SAPGUI 环境里,已知消息类和消息编号,快速找到抛出该消息的准确位置。

到了 SAP CRM WebClient UI 里,浏览器上看到的每一条消息,比如下图 Data Contains errors and cannot be saved, 仍然唯一对应后台一条消息记录 CRM_BUPA_BOL/036:
聊聊 SAP 产品 UI 上的消息显示机制_第6张图片
聊聊 SAP 产品 UI 上的消息显示机制_第7张图片

这条消息是由 Business Partner 应用负责维护的。

如何查找抛出 SAP WebClient UI 消息的对应后台代码位置,在 Jerry 的 SAP 社区博客上有介绍:

How to quickly locate the source code where a given message is raised in WebClient UI

到了 SAP Cloud for Customer,虽然 Partners 无法直接登录 ABAP 后台,然而仍然可以通过 Chrome 开发者工具,得到 UI 消息对应的 ABAP 后台消息记录的消息类和消息编号:

聊聊 SAP 产品 UI 上的消息显示机制_第8张图片

在 SAP Cloud for Customer 里,Partners 可以通过 Cloud Application Studio 新建 UI 消息:

聊聊 SAP 产品 UI 上的消息显示机制_第9张图片

在 ABSL 代码里,通过 raise 语句显示消息到 UI 上:

raise delivery_message.Create("S", this.OutboundDeliveryID);

运行时 delivery_message 定义的消息文本里的 &1,会被 this.outboundDeliveryID 字段的值取代。
聊聊 SAP 产品 UI 上的消息显示机制_第10张图片

到了 SAP S/4HANA 里,所有基于 Fiori Elements 创建的 Fiori 应用,共享同一套视图模板和控制器实现,因此大多数消息不再由应用程序各自定义,而由框架统一维护。

比如下列 S/4HANA 物料主数据维护时的错误消息:

Product Type 03 is not valid:
聊聊 SAP 产品 UI 上的消息显示机制_第11张图片

这条消息维护于所谓的 Business Suite Foundation Reusable Components 开发包中的消息类 /BOFU/CODE_LISTS 内部。

聊聊 SAP 产品 UI 上的消息显示机制_第12张图片

我们再来分析本文开头提到的 SAP Commerce Cloud 的消息显示定制化需求。

聊聊 SAP 产品 UI 上的消息显示机制_第13张图片

“感谢评论”的消息文本 ID 为 productReview.thankYouForReview,在 ProductReviewEffects 接收到 POST_PRODUCT_REVIEW_SUCCESS 这个 Action 之后通过 MessageService 显示到 UI 上。

聊聊 SAP 产品 UI 上的消息显示机制_第14张图片

一种最常规的二次开发思路就是,继承 SAP Commerce Cloud UI 标准发布的 ProductReviewEffects 类,重载其接收到 POST_PRODUCT_REVIEW_SUCCESS 之后的实现代码,将调用 MessageService 抛出感谢评论的代码删除掉即可。

然而 SAP Commerce Cloud UI 发布的所有 Effects 实现类都是不可扩展的,因此这条思路行不通。

Jerry 之前的文章:Jerry 在 2020 SAP全球技术大会的分享:SAP Spartacus 技术介绍的文字版,曾经介绍过 SAP 电商云 Spartacus UI,同 Commerce 后台交互的逻辑:

聊聊 SAP 产品 UI 上的消息显示机制_第15张图片

当时我着重阐述的,是上图右侧红色高亮区域的交互原理。

Spartacus 同 Commerce 系统的通信,通过 HTTP 协议调用 OCC API 完成。Connector 是 HTTP调用的发起者,维护了静态的配置信息,即 API endpoint.

比如,从 Commerce 系统读取产品主数据,读取的字段列表以 url 参数的形式出现在 API endpoint 里。这些字段列表可以在 Connector 的静态配置点里进行配置。

Connector 并不会直接同 Commerce 交互,而是把请求转发给 Adapter,具体通信由 Adapter 完成,Connector 只负责调度 Adapter. Spartacus 发布的 Adapter 默认使用 OCC Module,即 Commerce 标准的 OCC Restful API,但是客户也可以实现自己的 Adapter,连接 Commerce 之外的其他后台系统。

Connector 将 Adapter 取回的数据交给 NgRx Store 结构统一管理,后者的复杂度被 Facade 层所隐藏,而Spartacus UI 组件只会同 Facade 层交互,进行数据绑定和页面展示。这体现了关注点分离的设计原则。

再回到本文开头客户提出的定制化需求。为了实现该需求,我们需要深入了解下图红色高亮区域即 NgRx Store 和 SAP Spartacus Connector 交互的细节。

聊聊 SAP 产品 UI 上的消息显示机制_第16张图片

NgRx 是 Angular 基于 RxJs 的一个响应式状态管理库,包含下列核心概念,Jerry 会结合 SAP Commerce Cloud UI 对 NgRx 的实际使用情况来举例说明。

聊聊 SAP 产品 UI 上的消息显示机制_第17张图片

  • Action:其实就是编程领域的事件的别名。SAP Commerce Cloud UI 组件,能响应用户操作,通过组件的 Service 实例,投递出相应的 Action. Action 投递方和 Action 接收方是解耦的,彼此感知不到对方。

下图是 SAP 电商云产品明细页面评论区域的 Submit 按钮被点击之后,对应的 Service 类抛出 PostProductReview Action 的代码:
聊聊 SAP 产品 UI 上的消息显示机制_第18张图片

ProductActions.PostProductReview 是 NgRx Action 的一个子类,type 字段为 POST_PRODUCT_REVIEW,构造函数的参数 payload,定义了调用 Commerce OCC API 持久化用户评论需要传递的数据结构:

  • Effects:SAP Commerce Cloud UI Action 的接收方之一。下图 Effects 代码的语义是:接收类型为 POST_PRODUCT_REVIEW 的 Action(第46行),调用前文介绍的 Connector,向 Commerce 后台发起 OCC API 调用(第49行),根据 API 返回结果,分别投递评论保存成功或失败的 Action:
    PostProductReviewSuccess(第53行)
    PostProductReviewFail(第56行)

聊聊 SAP 产品 UI 上的消息显示机制_第19张图片

  • Store:Angular 应用维护在内存中的存储结构,存放了 SAP Commerce Cloud UI 所有组件的运行时数据。每当 Effects 调用 Commerce OCC API 拿到新的数据时,调用 Reducer,将增量数据填充到 Store 中去。
  • Reducer:本质上是一个有限状态自动机,每当收到代表来自 Commerce 后台的数据发生变化的 Action 时,状态机驱动对应的 Reducer, 根据新的 Action 包含的负载,对 Store 中的数据进行调整。

例如产品明细页面第一次渲染时,Effects 需要从 Commerce 后台读取所有的评论数据。读取成功后,抛出 LOAD_PRODUCT_REVIEWS_SUCCESS Action. 下图的 ProductReviewsReducer 接收到该 Action,从其 payload 中解析出实际评论数据并返回。这些返回的数据会被 NgRx 框架接收,并合并到 Store 中去。
聊聊 SAP 产品 UI 上的消息显示机制_第20张图片

  • Selector:纯函数,作为应用程序从 NgRx Store 中读取最新数据的接口。

理清楚 SAP Commerce Cloud UI 使用 NgRx 进行状态管理的细节之后,对于文章开头的客户定制化需求,也就不难实现了。

既然下图所示的 SAP Commerce Cloud UI 标准的 Effects 无法扩展,我们注意到 UI 组件的 Service 层,才是所有事件流的起始点,因此可以实现新的 Service,来替换 SAP 电商云 UI 标准的 Service,然后基于该定制化 Service,实现配套的 Effects 和 Action.

如此一来,标准的和用户评论相关的逻辑流(如下图 1-2-3 所示)将不会再得到执行,取而代之的是我们自己定制化的执行流,如下图 A-B-C 所示,其中蓝色的图例均为 Partners 需要开发的定制化代码。

聊聊 SAP 产品 UI 上的消息显示机制_第21张图片

这个解决方案的简要介绍:

(1) 创建新的 Action CustPostProductReview,CustPostProductReviewSuccess 和 CustPostProductReviewFail. 其实就是在标准的实现类之前,添加 Cust 的前缀。

(2) 创建新的 CustProductReviewService,继承标准的 ProductReviewService. 重载其 add 方法,抛出新的 CustPostProductReview Action.

聊聊 SAP 产品 UI 上的消息显示机制_第22张图片

(3) 新建 CustProductReviewsEffects,接收新的 Action CUST_POST_PRODUCT_REVIEW(第25行),仍旧调用 Connector 将用户评论持久化到 Commerce 后台(第28行),然后抛出新的 Action CustPostProductReviewSucess.

聊聊 SAP 产品 UI 上的消息显示机制_第23张图片

自定义 Effect 接收到新的 Action CUST_POST_PRODUCT_REVIEW_SUCCESS 之后,就可以进行自定义逻辑编写。

聊聊 SAP 产品 UI 上的消息显示机制_第24张图片

(4)将自定义的 Effect 和 Service 实现,通过 Angular 依赖注入框架,配置到对应的 Module 中。

聊聊 SAP 产品 UI 上的消息显示机制_第25张图片

本需求在 SAP Spartacus 3.1 版本测试通过。

更多阅读

更多Jerry的原创文章,尽在:"汪子熙":

你可能感兴趣的