
上个月帮同事排查一个 Fiori Elements 应用在生产环境找不到入口的问题折腾了一下午最后发现罪魁祸首是一个只有五六行配置的 Launchpad App Descriptor ItemLADI。这东西在 ABAP 环境里特别不起眼——RAP 自动生成应用的时候系统会顺带把 LADI 带出来很多开发者的印象就是“向导帮我建好了不用管”。可真要命的时候恰恰就是它。后来我又在好几个项目里陆续遇到 LADI 相关的问题自动生成的配置和目标映射对不上、手工改了以后应用启动不起来、想批量校验 URL 时 SQL 直接报错。这才意识到Launchpad App Descriptor Item 根本不是“生成完就可以忘记”的东西。这篇内容我会把从自动生成到手工扩展、再到排错落地的完整经验整理出来适合用 RAP 做 Fiori 开发的同事、负责 Launchpad 配置的 Basis/顾问以及想把应用入口管理得更规范的人。如果你之前对 LADI 的印象只是“一个自动生成的配置对象”这篇也许能帮你省下不少排查时间。1. LADI 到底管什么事先理解“启动描述符”这层皮1.1 一个配置项如何决定应用入口很多开发者的第一反应是应用能不能从 Fiori Launchpad 打开不是靠 PFCG 角色和 Groups 决定的吗这话对了一半。角色和 Groups 决定的是“谁能在启动台看到哪块磁贴”但磁贴点下去之后系统如何知道要跳到哪个应用、带什么参数、用什么方式加载这部分逻辑靠的就是 Launchpad App Descriptor Item。用大白话说LADI 就是应用在启动台里的“营业执照”。它本身不包含任何业务逻辑也不存放 Fiori 应用的代码它只说三件事这个应用属于哪个业务对象、允许执行什么动作、点击之后跳转到哪个目标。整个 Fiori Launchpad 的启动链路是用户点磁贴 → 启动台根据磁贴配置找到对应的语义对象和动作 → 再根据语义对象和动作找到 LADI → 从 LADI 的目标映射里读出目标 URL 或组件信息 → 最终拉起前端应用。这个过程看起来绕实际就是为了解耦。磁贴不需要知道某个应用在 ABAP 端的真实技术名称只需要声明“我要处理的是销售订单的创建场景”剩下的对应关系全交给 LADI。因此在 ABAP 环境里如果你想给同一个应用做两个不同的入口比如一个带默认参数的查询入口、一个干净的新建入口不需要复制代码只需要配置两个 LADI这就是它的价值。1.2 语义对象、动作和目标映射三个字段一台戏LADI 的核心结构可以拆成三层理解这三层基本就理解了一大半。第一层是语义对象Semantic Object。它是一段代表业务对象的稳定标识比如 SalesOrder、Material、PurchaseRequisition必须是全局统一的。注意这里说的“统一”指的是整个系统里大家约定同一个拼写不要一个应用叫 SalesOrder另一个应用叫 Sales_Order否则启动台就是一片混乱。第二层是动作Action。动作描述的是对这个语义对象执行的操作常见的有 display、create、edit、list、maintain 等。语义对象加动作组合在一起才是一个完整的启动坐标比如 SalesOrder-display 和 SalesOrder-create 是两种完全不同的入口。在 Launchpad 里配置磁贴时你要填的其实就是“语义对象 动作”这个坐标而不是直接写 URL。第三层是目标映射Target Mapping。坐标确定之后目标映射负责回答“到底往哪跳”的问题。一条目标映射里通常包含 Usage 类型常见的是 URL也有直接指定 UI5 组件的写法、目标地址、以及一组静态或动态参数。启动台拿到坐标后会从系统里查出对应的 LADI再去读目标映射最终完成跳转。你可以把一个坐标配置多条目标映射但必须通过不同的 Usage 或参数来区分启动台才能知道在哪个场景下选哪条。1.3 LADI 和 RAP 业务服务的关系RAP 开发的同事容易有一个误解LADI 是业务服务的一部分服务发布了 LADI 就自动存在改了服务 LADI 也会跟着变。实际上在 ADT 里 LADI 是一个独立的 repository 对象物理上存在独立的底层表里和业务服务、CDS 视图、行为定义这些对象是平级关系只是生成时机恰好跟着服务走。这带来两个直接影响。第一删除或重命名一个 RAP 业务服务时LADI 不会自动被删掉或改名字它就是个“死配置”留在系统里严重的还会在启动台配磁贴时造成对象选择混乱。第二你手工修改 LADI 里的目标 URL 时不需要重新激活业务服务改完激活 LADI 本身就能生效。这两个特性在很多项目里都被忽略后面讲排错的时候我会再展开。2. 自动生成不等于万事大吉三种常见生成路径与检查清单2.1 路径一RAP 向导在创建业务服务时顺带生成在 ADT 里走 RAP 开发流程时当你完成业务服务Business Service的创建和发布向导通常会询问是否同时生成 Fiori Launchpad 相关的配置这里就包含 LADI。如果项目里用的是标准模板生成的 LADI 名称一般会跟着业务服务名字走语义对象会自动取自 OData 服务的实体集名字动作默认为 display。这条路径适合大多数 Fiori Elements 应用因为标准 RAP 应用打开后就是列表加跳转详情一个 display 动作足够。但这里恰恰是很多人掉坑的地方向导生成后语义对象和动作都是“系统猜的”它不会读你的业务文档也不理解你们项目里“销售订单详情要用展示场景、销售订单列表要用查询场景”这种复杂约定。所以 RAP 自动生成的 LADI大概率只是“能跑”不一定“符合规范”。2.2 路径二Fiori Tools 的 Launchpad Config Generator如果你用的是 SAP Business Application StudioBAS或者 VS Code 加 Fiori Tools 插件那自动生成 LADI 的另一条常见路径是命令行执行Fiori: Launchpad Config Generator。这个工具可以针对已有 OData 服务生成一个完整的项目骨架其中就包含 Launchpad 配置产物。它跟 RAP 向导有一点不同你可以更自由地指定语义对象、动作、目标 URL 的前缀还能选择生成后的产物是直接进 ABAP 环境还是留在本地。实测下来这个工具更适合那些“Fiori 前端项目已经手工搭好、只是需要快速补一个启动描述”的场景。比如你的项目在 BAS 里开发 UI5后端 OData 服务早就建好了这时候用 Fiori Tools 生成 descriptor 会比打开 ADT 手工一点一点填快得多。不过要注意生成出来的 LADI 同样需要你检查它的包归属BAS 默认放的包往往和项目主线包不一致激活前最好先确认传输请求。2.3 路径三经典环境下的套壳与补丁式生成在经典 NetWeaver 环境里除了上述两条现代化路径还能见到一些“补丁式”的生成方式。比如有些项目没有上 RAP而是用老式 Gateway 服务加 SAPUI5 前端开发会直接在事务码相关配置入口里维护启动信息或者拿现成 LADI 复制出一个新对象再改参数。这种方式的优点是快缺点是很容易复制出“脏数据”复制出来的 LADI 里往往残留旧项目的 URL 前缀、旧语义对象名甚至目标映射里带着过期的 SAPUI5 组件名。我个人建议经典环境里如果走复制生成复制后第一件事不是改名字而是重置目标映射里的全部 URL 和参数把所有键值重新填一遍别懒。2.4 生成之后必须盯住的三个检查点不管走哪条路径自动生成之后我都建议花两分钟检查三个点。第一个是包。LADI 有没有放到正确的工作包和传输请求里这决定了你改动能不能传到生产环境。第二个是语义对象和动作是否符合项目命名规范。系统自动从实体集名字抓出来的语义对象经常是全大写带下划线比如 SALES_ORDER_AUTO跟项目里手工维护的其他对象风格不一致启动台上会很难匹配。第三个是目标映射的 URL 是否以实际 OData 服务的服务路径开头。Fiori Tools 生成时有时会把 URL 写成默认占位符直接拿过来用必然是启动报错。这三个点检查一遍后面能少踩一半坑。3. 手工扩展从“能用”到“好用”的实操细节3.1 在 ADT 里新建和编辑 LADI 的正确姿势如果你决定完全手工创建一个 LADI在 ADT 里的操作路径是右键项目 → New → Other → 搜索Launchpad App Descriptor Item。填好名称和描述之后最关键的步骤是选包。LADI 所在的包决定了它在软件组件里的位置也决定了走哪条传输链建议和对应的业务服务放同一个包方便后续 release 和携带传输。创建完成后编辑器主界面有三个区域基本信息区语义对象、动作、维护语言、目标映射区、参数配置区。很多人在这里会犯一个低级错误基本信息区里语义对象和动作填错大小写或者动作填了系统不认识的单词。这个字段不是完全自由文本它要能跟后台配置的语义对象库对应上。稳妥的做法是先在系统里翻一下同类应用用的什么语义对象照着抄。3.2 目标映射与参数透传让同一个入口开出不同效果手工扩展比自动生成多出来的能力集中在目标映射和参数这一块。以 Fiori Elements 应用为例自动生成的 LADI 里 target mapping 通常就是一行 URL/sap/opu/odata/sap/xxx。手工扩展时你可以追加静态参数例如把 UI5 组件名写进目标配置里或者给应用指定默认语言参数。比较实用的场景是参数透传。比如你在启动台配了一个“销售订单详情”磁贴用户从报表跳转到该磁贴时希望把订单号带过去。这个场景下你不能只写死 URL而是需要在 LADI 里配置输入参数和输出参数让启动台从磁贴上下文取值再拼到目标 URL 的 query string 里。这部分配置叫法在工具里可能叫“参数映射”实际填的时候注意输入参数名必须和磁贴里定义的名字一致输出参数名必须和应用前端读的参数名一致中间的对应关系填错一点点数据就传不过去。3.3 多目标映射的使用场景一套语义对象多条跳转路径一条 LADI 可以配置多条目标映射而且这不是摆设。常见的多目标映射场景是同一个 SalesOrder-display 坐标在 PC 端希望用 Web 组件打开在移动端希望用一个轻量化的 URL 打开。此时你可以在同一条 LADI 下配两条 target mapping分别用不同的 Usage 来标识启动台会根据运行端自动选择合适的映射。这里要特别注意一点多条映射之间必须确保至少有一个区分字段不同否则系统有时会乱选。我踩过的坑是给同一个坐标配了两条 URL 一模一样的映射结果修改其中一条后启动台里点磁贴时它取了另一条表现出来的现象就是“我改了配置怎么不生效”。后来把区分字段补上问题立刻消失。4. 实战排错LCHR 字段、失同步和不生效的诡异问题4.1 “the column url cannot be used in sql due to its type lchr”一次 LADI URL 校验脚本的翻车前阵子有个需求批量检查系统里所有 LADI 的 URL 是否符合规范比如是否都以/sap/opu/odata/开头、是否包含非法字符。这需求听起来简单我第一版代码直接对着 LADI 底层表写了条 OPEN SQL在 SELECT 列表里选了 URL 字段。编译没问题一运行就报错the column url cannot be used in sql due to its type lchr。这个报错信息很多人会看得莫名其妙其实原因很直接。底表里 URL 字段的类型是 LCHR也就是“长字符”这种列和普通 CHAR、VARCHAR 不一样ABAP 的 OPEN SQL 对它有严格的使用限制——你不能在 SELECT、WHERE、GROUP BY 这类常规 SQL 上下文里直接拿它当普通字符列用。这是数据库层的类型约束不是你 SQL 语法写错了。解决思路有这么几种。一种是在 OPEN SQL 里用 CAST 或字符串函数把 LCHR 转成普通字符比如CAST( url AS CHAR( 128 ) )转完就能放入 SELECT 列表。另一种更干净的做法是写个 ABAP 方法把整条记录读出来以后在应用层解析绕开 SQL 层的类型限制。我自己后来是两种结合需要批量做模糊匹配时用 CDS 视图先把 URL 字段 cast 成普通字符再在 SELECT 里查那个视图动态性和性能都兼顾了。4.2 结构变更后 LADI 静默失同步RAP 项目推进后经常出现这样的场景开发阶段叫 ZZ_SALES_ODATA 的服务到了重构阶段改成了 ZZ_SALES_V2然后发布新服务更新了前端组件。结果系统里老的 LADI 里 URL 还指着 ZZ_SALES_ODATA新服务已经不存在了。这种情况下启动台里点磁贴Fiori 前端会尝试请求老服务路径轻则空白重则直接 404。根因就是前面说的LADI 是独立对象不会跟着 RAP 服务自动改名。所以每次重构完业务服务我习惯全局搜一遍 LADI 目标映射里的旧服务路径把所有引用统一刷成新路径。这个搜索在 ADT 里可以直接用搜索功能翻 LADI 内容或者在底层表里跑一个范围查询。刷新之后记得重新激活并走一遍传输别只改本地。还有一个相对隐蔽的失同步场景是语义对象变了。比如需求调整后销售订单的场景从 display 拆成了 display 和 detail 两个动作你新增了 action但老的磁贴还配着 display。这时候磁贴工具里能查到语义对象但动作匹配不上任何 LADI启动时同样报错。排查方式是从报错串里的语义对象反查确认有没有对应的 LADI 存在。4.3 语义对象冲突与启动缓存玄学背后的确定性原因有段时间我很疑惑同一个语义对象动作明明只有一条 LADI怎么有时候生效有时候不生效后来发现两个原因。第一是语义对象冲突系统里存在两条 LADI语义对象和动作同名只是目标映射不同启动台在某些条件下取了旧的一条。解决办法没有捷径就是全系统搜同名配置把多余的那条停用或删除。第二个原因是启动缓存。Fiori Launchpad 为了性能会缓存一部分启动配置。修改 LADI 之后用户端如果不刷新缓存看到的仍然是旧配置。这个现象在开发环境特别耗时间明明刚才改了配置激活了浏览器里还是老样子。我现在的习惯是改完 LADI 后不急着验证先把启动台缓存清一遍再测避免被假象带偏。运维同学如果收到“改了配置不生效”的反馈第一反应也该先问对方清没清缓存。5. 落地建议命名、版本和团队协作的一点点经验5.1 命名规范与注释建议LADI 是那种“不写注释系统也能跑”的对象但项目到了一定规模之后没有规范的命名会让人抓狂。我见过一个系统里有十几个语义对象长得一模一样后缀只是_1、_2、_COPY的区别排查的时候根本没眼看。建议的命名格式是模块前缀加业务对象再加场景后缀比如ZSD_SALESORDER_DISPLAY_LADI。语义对象尽量与标准业务对象对齐不要一个项目里出现两套叫法。描述字段里写清楚这个 LADI 服务于哪个 Fiori 应用、目标组件是什么别嫌啰嗦三个月后这个描述能救你一次。5.2 abapGit 与传输顺序LADI 是个适合纳入版本管理的对象在 abapGit 里它的导出内容就是一段 XML 描述包含语义对象、动作、目标映射和参数。这意味着你可以很方便地在不同系统之间对比两个 LADI 的差异也可以做代码评审。我们团队现在的做法是把所有 LADI 纳入 abapGit 仓库每次改动都生成独立的提交记录review 的时候能直接看到 URL 变更、参数变更比在系统里凭记忆对比强太多。传输顺序上面有个小提醒LADI 最好跟着它引用的业务服务同一个批次传输或者等服务激活并发布后再传 LADI。反过来传的后果是生产系统里 LADI 已经激活但服务还没发布启动台点击时找不到 OData 服务报错信息还不直接指向原因。5.3 上线检查清单附表格最后把我每次上线前对照检查的项目表格化直接抄走用就行。检查项具体内容通过标准语义对象与项目规范完全一致启动台能查到该对象动作与磁贴配置匹配语义对象动作能匹配到唯一 LADI目标映射 URL指向已发布业务服务服务路径可访问参数配置静态/动态参数完整参数名与应用端读取一致包与传输与业务服务同包同批次abapGit 对比无残留差异缓存验证前已清理启动缓存用户端刷新后可正常启动冗余清理无重复语义对象动作组合全系统搜索无同名 LADI这套检查清单看起来朴素但我已经靠它拦住过三次生产事故了。LADI 不是核心业务代码出错概率却一点都不低原因无外乎三点自动生成的内容没人复核、手工扩展的参数没人验证、上线前没人系统性检查一遍。写到这里想起那次排查生产环境找不到应用入口的经历。当时我一度怀疑是角色配置问题结果在一个和业务代码毫无关系的配置对象里找到了答案。后来我养成了一个习惯每建成一个 RAP 服务第一件事不是急着写前端而是先把 LADI 打开确认语义对象、动作、URL 这三样东西清清楚楚。这十几秒的时间比事后排查一小时值多了。