本章介绍如何用执行链(Execution Chain)把相关页面处理整理成可读、可维护的流程,并把耗时任务交给 APEX 后台执行,让用户提交页面后可以继续工作。

18 组织逻辑与后台执行#

执行链是一种页面处理类型,用来容纳一组子处理。执行链自己的服务器端条件决定这些子处理是否运行;子处理可以执行 PL/SQL、调用 API 或 REST 服务、启动工作流、打印报表、发送邮件或推送通知、装载数据等。执行链还可以嵌套,因此只要命名清楚,页面处理树本身就会成为业务逻辑的结构化大纲。

当某个执行链启用 Run in Background 后,APEX 会把这组处理排入后台作业。用户会话继续执行后续页面处理并返回页面;后台作业则在独立会话中运行,使用页面会话状态的私有副本。后台作业可以处理上传文件、报告进度、完成后通知用户,也可以按用户、应用、工作区或上下文值限制并发,避免资源争用或同一业务对象被重复处理。

18.1 预览 HR 代表使用的 Employee Excellence 页面#

官方示例使用 Woods Clinic 的 Employee Excellence 页面说明本章场景。HR 代表 Susan 可以在页面上选择员工,发送入职欢迎资料包(Welcome Packet),也可以启动一个 AI 辅助的员工卓越奖评审流程(Review for Award)。欢迎资料包处理很快;评审流程较长,因为它需要收集反馈和外部指标、用 AI 归一化和总结信息、按公司评分规则分析表现、用机器学习预测获奖资格,并在员工符合条件时生成证书并发送邮件。

图 18-1 HR 代表在 Employee Excellence 页面选择员工并发起欢迎资料包或奖项评审。

评审流程被放入后台后,Susan 不需要等待所有步骤完成。页面可以显示她已发起的后台评审,包括当前状态、百分比和进度条;如果员工符合条件,系统会在流程完成后把证书作为邮件附件发送给员工。

图 18-2 页面上的 Award Review Progress 区域显示后台评审的进度和状态。

复现要点:准备一个包含员工选择项、动作选择项、隐藏员工姓名和邮箱项的页面。页面应包含“快速欢迎资料包”和“后台奖项评审”两类提交动作。验证时分别发起短任务和长任务,确认短任务立即完成,长任务进入进度列表。

18.2 使用执行链梳理页面处理逻辑#

Employee Excellence 页面的页面处理不是平铺的一串 Process,而是通过执行链分组和嵌套。这样开发者在 Page Designer 的 Processing 树中就能直接看出业务分支:何时发送欢迎资料包、哪些步骤只对经理执行、哪些材料只发给 HR 代表,以及最后如何打包并邮件发送。

18.2.1 在提交后设置上下文#

用户点击处理按钮提交页面后,页面先运行一个 After Submit 处理 Get Employee Name and Email。它根据用户在 P34_EMPLOYEE_EMPNO 选择的员工编号,把员工姓名和邮箱写入隐藏页项,供后续执行链和子处理复用。

select ename, :G_DEMO_EMAIL
  into :P34_EMPLOYEE_ENAME, :P34_EMPLOYEE_EMAIL
  from eba_demo_emp
 where empno = :P34_EMPLOYEE_EMPNO

操作路径:在 Page Designer 中打开目标页面,进入 Processing 区域,新建或检查 After Submit 处理。它应在依赖这些值的执行链之前运行。验证时选择一名员工并提交,检查会话状态中 P34_EMPLOYEE_ENAMEP34_EMPLOYEE_EMAIL 已被填入。

18.2.2 让处理按条件运行#

执行链也可以有服务器端条件。示例中的 If Sending Welcome Packet... 页面处理类型为 Execution Chain,它的条件是只有 P34_ACTION 选择列表值等于 WELCOME 时才运行子处理。

图 18-3 执行链用服务器端条件控制整组子处理是否执行。

验证检查:选择 WELCOME 动作时,欢迎资料包链应运行;选择其他动作时,该链及其子处理不应运行。

18.2.3 排列子处理顺序#

执行链内的子处理按顺序运行。可以在同一级中拖放调整顺序,也可以修改子处理的 Sequence 属性。若要把子处理移动到另一个执行链,修改它的 Execution Chain 属性即可;设为 None 则回到 Processing 树根层级。

图 18-4 子处理按 Sequence 执行;示例中 Add General Notices 先调用 EBA_DEMO_WOODSHR_WELCOME.ADD_GENERAL_NOTICES

预期结果:欢迎资料包的材料收集、附加、压缩和发送邮件步骤必须以业务依赖顺序运行,不能先发送再生成附件。

18.2.4 嵌套执行链#

执行链可以嵌套到任意需要的层级。示例中的 If User is Manager...If Sending Welcome Packet... 的子执行链,它通过 PL/SQL 布尔表达式判断所选员工是否管理其他人:

eba_demo_woodshr_welcome.manages_others(:P34_EMPLOYEE_EMPNO)

如果条件为真,子处理会把管理意识、团队发展等只适用于经理的材料加入欢迎资料包。

图 18-5 嵌套执行链让业务条件和处理步骤保持在同一棵可读的处理树中。

18.2.5 使用描述性处理名称#

执行链的价值很大一部分来自可读性。请用业务动作命名,例如 If User is HR Rep...Zip Materials from CollectionEmail Recipient,不要使用 Process 1 这类无法说明意图的名称。示例最后通过 Send E-Mail 原生处理把 ZIP 文件发送给员工,收件人使用替换语法 &P34_EMPLOYEE_EMAIL.

select blob001 as contents,
       'woods_newhire_notices.zip' as file_name,
       'application/zip' as mime_type,
       null /* Normal Attachment */ as content_id
  from apex_collections
 where collection_name = 'NOTICES_ZIP'
 fetch first row only
图 18-6 原生 Send E-Mail 处理可以通过 Attachment SQL 附加集合中的文件。

18.3 把工作移到后台#

长时间运行的页面处理不应让用户一直等待。把执行链启用 Run in Background 后,APEX 会尽快调度后台作业,页面提交可以继续往下执行。示例中的 Review for Reward (in Background) 链组合了 API 调用、AI 服务、机器学习、报表生成、邮件发送和推送通知。

18.3.1 配置执行链在后台运行#

在执行链上启用 Run in Background 后,APEX 会为运行实例分配唯一的执行 ID。若需要在页面上保存这个 ID,可以设置 Return ID into Item。若需要把后台作业关联到业务对象,可以设置 Context Value Item。Employee Excellence 页面把 P34_EMPLOYEE_EMPNO 作为上下文值,因此每个后台评审作业都能对应到正在处理的员工编号。

图 18-7 Context Value Item 的值会出现在 APEX_APPL_PAGE_BG_PROC_STATUS.CONTEXT_VALUE 中。

验证检查:提交评审后,页面应快速返回;后台状态视图中应能看到进程名、执行状态以及员工编号上下文值。

18.3.2 拆解奖项评审流程#

Review for Reward (in Background) 的前三个子处理都是 Invoke API:先收集同事、经理反馈和外部系统指标;再调用 AI 服务归一化并汇总信息;最后用基于历史数据训练的机器学习模型预测员工是否符合卓越奖条件。第三个处理会把函数结果写入隐藏页项 P34_EMPLOYEE_REVIEW_RESULT

图 18-8 Invoke API 可以把函数返回值映射到页面项。

后续 If Qualified... 执行链只在 P34_EMPLOYEE_REVIEW_RESULT 等于 QUALIFIED 时执行。条件成立时,它生成证书、发送邮件,并向获奖员工的已订阅设备发送推送通知。

图 18-9 用执行链条件控制只对符合条件的员工运行后续通知和证书步骤。

18.3.3 在后台生成个性化证书#

Generate Certificate 子处理通过 Invoke API 调用 EBA_DEMO_WOODSHR_REWARD.GENERATE_CERTIFICATE。该过程把员工编号写入报表查询引用的应用项,按静态 ID 生成 PDF 文档,创建或清空 EMP_EXCELLENCE_CERTIFICATE 集合,并把生成的 PDF BLOB 放入集合,供邮件附件使用。

-- In package eba_demo_woodshr_reward
procedure generate_certificate(p_empno in number) is
  l_certificate_pdf blob;
begin
  apex_background_process.set_status('Generating Certificate');
  apex_session_state.set_value('G_EMPLOYEE_EXCELLENCE_EMPNO', p_empno);

  l_certificate_pdf := apex_print.generate_document(
    p_application_id         => V('APP_ID'),
    p_report_query_static_id => 'emp_excellence_certificate');

  apex_collection.create_or_truncate_collection('EMP_EXCELLENCE_CERTIFICATE');
  apex_collection.add_member(
    p_collection_name => 'EMP_EXCELLENCE_CERTIFICATE',
    p_blob001         => l_certificate_pdf);

  apex_background_process.set_progress(p_totalwork => c_steps, p_sofar => 8);
  apex_background_process.set_status('Generating Certificate Completed');
end generate_certificate;

验证检查:在后台链运行到该步骤后,集合中应有一条 PDF BLOB;下一步邮件处理能读取同一个后台会话中的集合内容。

18.3.4 发送带附件或内联图片的邮件#

Email Certificate 是原生 Send Email 处理。它用隐藏页项 P34_EMPLOYEE_EMAIL 作为收件人,正文可以使用替换字符串个性化内容。附件 SQL 必须按顺序返回四列:文件 BLOB、文件名、MIME 类型、Content ID。普通附件的 Content ID 用 null;若要在 HTML 正文中内联引用图片,则为每行图片提供不同的非空 Content ID。

<img src="cid:company_logo" alt="Woods Clinic Logo">
图 18-10 Send Email 处理使用 Attachment SQL 附加证书文件。
图 18-11 员工 JONES 在邮件客户端中看到奖项说明和证书附件。

18.3.5 向已订阅设备发送推送通知#

最后一个子处理使用原生 Send Push Notification。与邮件不同,推送通知的 To 属性使用用户名,例如 SMITH,而不是邮箱地址。APEX 只会把通知实际发送到用户已选择接收推送的设备。

图 18-12 符合条件的员工可以在已订阅设备上收到获奖推送。

18.4 串行化后台处理作业#

启用后台执行链的 Serialize 开关后,APEX 引擎会限制同类型后台作业的并发。如果同时配置 Context Value Item,串行化会按上下文值生效。示例把 P34_EMPLOYEE_EMPNO 作为上下文值,因此不同员工的评审可以并行,同一员工的重复评审则会被限制。

图 18-13 串行化属性可配置同一上下文值已在运行时的处理方式和错误消息。

当某个 HR 代表尝试再次为已经在后台处理的员工发起评审时,示例把 When Already Running 设置为 Error,因此页面会显示错误,而不是静默排队或重复执行。

图 18-14 对同一员工重复发起奖项评审时显示冲突错误。

工程检查:串行化能减少重复后台作业,但不能替代数据库约束、锁和幂等设计。涉及扣款、发信、写入业务结果的后台流程仍应能安全处理重试和重复提交。

18.5 理解克隆会话状态#

后台作业启动时,APEX 会把用户会话状态克隆到后台作业的专用会话中。后台作业之后对会话状态的修改只影响自己的副本,不会改变用户当前会话,也不会影响其他后台作业。

18.5.1 隔离后台会话状态#

APEX 为每个后台作业分配唯一执行 ID,创建后台处理专用的新会话,并把用户会话状态复制过去。从这个时间点开始,后台处理读取和写入的是自己的会话状态。用户在前台页面继续修改页面项,不会自动同步到已经启动的后台作业。

实践建议:后台作业需要的关键输入应在提交前写入明确的页项、上下文值或业务表。不要让后台任务依赖用户稍后可能改变的前台页面状态。

18.5.2 配置临时文件处理方式#

后台处理上传文件时,应配置执行链的 Temporary File Handling。可选值包括:Ignore,文件留在用户会话中;Move,文件移动到后台执行链的新会话;Copy,文件保留在用户会话并复制到后台会话。选择 Move 或 Copy 时,还要在 Temporary File Items 中提供文件上传或图片上传项的逗号分隔列表。

后台会话读取这些临时文件的方式与用户会话相同,仍然使用 APEX_APPLICATION_TEMP_FILES 表。

18.6 报告后台进度#

APEX_APPL_PAGE_BG_PROC_STATUS 视图提供后台作业的状态、动态状态消息、已完成工作量和总工作量。应用可以用它把后台任务的进度展示给用户,而不是让用户只看到“正在处理”。

18.6.1 从 PL/SQL 报告后台状态#

后台 PL/SQL 可调用 APEX_BACKGROUND_PROCESS.SET_PROGRESSAPEX_BACKGROUND_PROCESS.SET_STATUS 更新当前进度。示例把奖项评审拆成 9 个工作单位,完成每个阶段时更新 SOFAR 和状态消息。必要时可用 GET_CURRENT_EXECUTION 获取当前后台执行的执行 ID、状态、最后状态消息、SOFARTOTALWORK

-- In package eba_demo_woodshr_reward
c_steps constant pls_integer := 9;

procedure gather_performance_information(p_empno in number) is
begin
  apex_background_process.set_progress(p_totalwork => c_steps, p_sofar => 0);
  apex_background_process.set_status('Gathering Project Management Details');
  -- ...
  apex_background_process.set_progress(p_totalwork => c_steps, p_sofar => 1);
  apex_background_process.set_status('Gathering 360 Feedback Information');
  -- ...
  apex_background_process.set_progress(p_totalwork => c_steps, p_sofar => 2);
  apex_background_process.set_status('Gathering Attendance Records');
  -- ...
  apex_background_process.set_progress(p_totalwork => c_steps, p_sofar => 3);
  apex_background_process.set_status('Gathering Completed');
end gather_performance_information;

18.6.2 访问后台处理状态#

如果执行链配置了 Return ID into Item,可以用保存下来的执行 ID 按 EXECUTION_ID 查询 APEX_APPL_PAGE_BG_PROC_STATUS。该视图还包含多个关键关联字段:PROCESS_ID 指向后台作业对应的页面处理定义;CURRENT_PROCESS_ID 指向执行链中当前正在运行的子处理;SESSION_ID 是发起后台处理的用户会话;WORKING_SESSION_ID 是后台处理自己的工作会话。

图 18-15 APEX_APPL_PAGE_BG_PROC_STATUS 与页面处理和会话视图之间的关系。

18.6.3 查询后台进度#

Employee Excellence 页面的 Award Review Progress Classic Report 查询当前页面中名为 Review for Reward (in Background) 的后台作业,并只显示 ENQUEUEDSCHEDULEDEXECUTING 状态。百分比由 SOFAR / TOTALWORK 计算;进度条列保留 0 到 1 的小数,供 Percent Graph 列类型使用。

select e.ename,
       case
         when bgp.sofar is not null and bgp.totalwork is not null
         then round(bgp.sofar / bgp.totalwork * 100) || '%'
         else '⏳'
       end progress,
       nvl(round(bgp.sofar / bgp.totalwork * 100), 0) progress_bar,
       coalesce(bgp.status_message, '⏳') as status_message
  from apex_appl_page_bg_proc_status bgp
  join eba_demo_emp e on e.empno = to_number(bgp.context_value)
 where bgp.process_name = 'Review for Reward (in Background)'
   and bgp.application_id = :APP_ID
   and bgp.page_id = :APP_PAGE_ID
   and bgp.status_code in ('ENQUEUED', 'SCHEDULED', 'EXECUTING')

预期结果:报表按员工显示正在排队、已调度或执行中的后台评审;已成功、失败或中止的历史作业不进入这个“当前进度”列表。

18.6.4 用进度条显示后台进度#

Classic Report 列类型 Percent Graph 可以把 0 到 1 的小数显示为进度条。需要时可以在 Appearance 区域配置背景色、前景色和条宽。

图 18-16 把 PROGRESS_BAR 列类型设为 Percent Graph 后,用户能直观看到后台进度。

18.6.5 让经典报表最后一列占满剩余宽度#

Award Review Progress 报表包含员工姓名、百分比、进度条和状态消息。状态消息通常最需要空间,因此示例给 Classic Report 区域添加自定义 CSS 类 stretch-last-column,让最后一列使用页面剩余横向空间。

图 18-17 状态列占据经典报表的剩余宽度。
图 18-18 在区域 CSS Classes 中配置 stretch-last-column
/* In app-wide CSS Static Application File app.css */
.stretch-last-column .t-Report-report {
  width: 100% !important;
  table-layout: fixed;
}

.stretch-last-column .t-Report-tableWrap {
  width: 100%;
}

.stretch-last-column .t-Report-report th:not(:last-child):not([data-col-width]),
.stretch-last-column .t-Report-report td:not(:last-child):not([data-col-width]) {
  width: 120px;
}

.stretch-last-column .t-Report-report th:last-child:not([data-col-width]),
.stretch-last-column .t-Report-report td:last-child:not([data-col-width]) {
  width: auto;
}

验证检查:没有显式 Cell Width 的非最后列保持可读宽度,最后一列展开;若某列设置了 Cell Width,该设置仍应优先。

18.6.6 把文本限制为固定行数#

Universal Theme 提供一组 u-lineclamp-n CSS 类,可把不确定长度的文本限制在固定行数。示例给 STATUS_MESSAGE 报表列添加 u-lineclamp-1,让后台状态消息只显示一行;文本过长时,浏览器自动显示省略号。n 可取 1 到 5。

图 18-19 在报表列 CSS Classes 属性中使用 u-lineclamp-1
图 18-20 状态消息超过限制行数时显示省略号。

18.7 在后台装载数据#

用户上传的数据如果处理时间较长,可以通过后台执行链装载。典型配置包括:启用 Run in Background,把 Temporary File Handling 设置为 MoveCopy,并把 Data Loading 作为子处理。需要时,同一执行链还可以在装载完成后发送邮件或推送通知。

后台作业在启动时拥有用户会话状态的私有副本,因此可以在通知中引用 P34_EMPLOYEE_EMAILP34_EMPLOYEE_NAMEAPP_USER 等当时的页项和值。

18.8 限制和节流后台作业#

后台执行链作业可以在多个层级限制。最具体的是执行链的 Executions Limit,超过后对该用户报错。应用定义中的 Maximum Background Page Process Jobs 会限制本应用可同时调度的后台作业,超过的作业排队等待。工作区级别也可设置 Maximum Background Page Process Jobs,由实例管理员用于控制整个工作区的后台作业并发;该值设置后会覆盖实例参数 MAX_PROCESS_SCHEDULER_JOBS_DEFAULT

图 18-21 当应用最大后台作业数为 4 时,额外的员工奖项评审会等待调度。

验证检查:连续发起多个后台任务,确认前几个进入执行状态,其余作业显示排队或等待调度状态;用户界面应明确说明任务没有丢失,只是在等待资源。

18.9 调整后台作业资源#

APEX 实例管理员可以为后台处理作业配置独立的 job class,以便在受资源约束的实例中给长时间后台任务分配不同资源策略。需要由 SYS 或具有 APEX_ADMINISTRATOR_ROLE 的用户执行配置。

-- Run as SYS or user with APEX_ADMINISTRATOR_ROLE
begin
  apex_instance_admin.set_parameter(
    p_parameter => 'BACKGROUND_PROCESS_JOB_CLASS',
    p_value     => 'CUSTOM_BG_PROC_CLASS');
end;

边界说明:这是实例级管理配置,不属于普通应用开发者在 Page Designer 中完成的页面配置。实施前应评估数据库资源、作业类权限和其他工作区影响。

18.10 调试后台处理#

调试后台执行链时,主要使用 Debug Viewer 和 Monitor Activity 页面。你可以查看后台作业、必要时终止作业、检查后台作业的工作会话状态,并追踪失败作业的错误细节。

18.10.1 查看应用后台处理#

从开发者工具栏选择 Debug > View Debug 打开 Debug Viewer。在 Session 标签页中,把 View 选择为 Background Executions,点击 Set,即可查看当前应用的后台处理。列表会显示每个作业的状态、当前状态消息、So far、Total work 和 Context 值。示例中的上下文值是员工编号。

图 18-22 Debug Viewer 可筛选后台作业状态,并可通过 Terminate 中止作业。

18.10.2 排查后台处理错误#

后台作业遇到未预期错误时,先在 Debug Viewer 的 Session 标签页把 View 设为 Background Executions,筛选 Failed 状态。随后切换到 Debug 标签页,按 Timestamp 倒序排序,打开可疑后台处理对应的 debug trace。

图 18-23 先从失败状态的后台作业定位候选执行。
图 18-24 按时间定位与后台作业对应的 Debug Trace。

Debug Trace 时间线上红点表示错误。点击第一个红点可以跳到相关日志位置。示例中堆栈显示 EBA_DEMO_WOODSHR_REWARD 包第 43 行在调用 PREDICT_AWARD_QUALIFICATION 的 Invoke API 中遇到 NO_DATA_FOUND 异常。必要时可以开启调试并在代码中加入 APEX_DEBUG.INFO,用更多上下文缩小问题范围。

图 18-25 点击第一个错误红点,优先查看最早失败位置。
图 18-26 错误详情和调用栈帮助定位失败的包、行号和处理步骤。

18.10.3 查看后台处理工作会话状态#

要查看后台处理的会话状态,可从 App Builder 中带扳手的人形菜单进入 Monitor Activity。先找到类似 SUSAN 的用户会话,点击 session id 查看详情。

图 18-27 从活动会话进入某个用户会话的详情。

在 Session Detail 页面选择 Background Processing 标签页,可以看到该用户会话发起的已调度和运行中作业。点击 Work Session ID 列中的会话 ID,即可进入后台作业自己的工作会话。

图 18-28 Work Session ID 指向后台处理的独立工作会话。

进入后台工作会话详情后,打开 Session State Item Values 标签页,可查看未加密的会话状态值。加密项不能直接在该页面查看;如果调试必须看到相关值,应临时在代码中加入受控的 APEX_DEBUG.INFO 调试输出,问题排查后再移除。

apex_debug.info('### DEBUGGING (%s) (%s)', expression1, expression2);
图 18-29 在后台处理的 Work Session 中检查页面项和应用项状态。