20 通知最终用户#
Oracle APEX 应用可以通过三类方式通知最终用户:页面上的成功与错误消息、电子邮件,以及推送通知。这些通知既可以通过声明式属性完成,也可以在 PL/SQL 或 JavaScript 代码中触发。
学习本章时,可以把通知分成两个问题:首先,用户当前正在页面上操作时,如何立即告诉他操作结果;其次,用户不在当前页面时,如何通过邮件或设备通知把消息送达。
官方来源:Notifying End Users
20.1 显示成功与错误消息#
成功消息和错误消息用于反馈操作是否完成。成功消息通常告诉用户保存、提交、处理等动作已经成功;错误消息则应说明失败原因,并尽量给出可修正的方向。
APEX 支持在页面进程、验证、动态操作、服务器端代码和客户端 JavaScript 中显示消息。对于数据库约束、触发器异常或 APEX 引擎错误,还可以配置错误处理函数(Error Handling Function),在错误显示给用户前改写消息内容。
官方来源:Displaying Success and Error Messages
20.1.1 配置成功与错误消息#
页面进程可以在属性中直接配置成功消息(Success Message)和错误消息(Error Message)。最简单的方式是在属性里写固定文本,也可以使用页面项替换语法,让消息带上当前页面数据。
Your order number is &P113_ORDER_ID. and will ship &P113_SHIP_DATE.
如果应用需要多语言消息,建议把消息定义为可翻译文本消息(Text Message),再在页面进程中引用。下面的写法引用名为 ORDER_CONFIRMATION 的文本消息,并传入 ordnum 与 ships 两个占位符。
&{ORDER_CONFIRMATION ordnum="&P113_ORDER_ID." ships="&P113_SHIP_DATE."}.
当文本消息本身包含 HTML 标记时,使用 !RAW 修饰符可以避免显示时被转义。
&{ORDER_CONFIRMATION ordnum="&P113_ORDER_ID." ships="&P113_SHIP_DATE."}!RAW.
如果一次提交中有多个页面进程都产生成功消息,APEX 会把这些成功消息合并显示。
成功消息可以包含简单 HTML。例如可以用换行和加粗标记控制显示格式。
<br>Success <strong>Message</strong> 2
错误消息的行为不同。多个页面进程的错误消息不会继续累积;第一个失败的进程会停止后续处理,并显示该错误。
如果消息内容需要先由代码计算,可以在前置页面进程中把结果写入隐藏页面项,例如 P113_COMPUTED_MESSAGE,再在同一进程或后续进程的消息属性中通过 &P113_COMPUTED_MESSAGE. 引用。
20.1.2 使用验证一次显示多个错误#
APEX 验证(Validation)适合处理用户输入规则。提交页面时,所有失败的验证都会一起显示,用户可以一次看到需要修正的全部问题。
字段相关的验证可以关联到具体页面项,并显示在字段附近;没有绑定字段的验证则显示在页面通知区域。这样既能提供全局错误列表,也能把具体错误放在用户需要修改的位置。
官方来源:Using Validations for Multiple Errors
20.1.2.1 通过返回 True/False 做验证#
类型为 PL/SQL Expression 的验证会把表达式结果作为通过与否的判断:表达式返回 true 时数据有效;返回 false 时显示验证的 Error Message。
例如,验证“两个字段不能都以 4 开头”可以写成下面的表达式。它的含义是:不是两个页面项的首字符都等于 4。
not (
substr(:P3_MULTIPLE_OF_TEN,1,1) = '4'
and substr(:P3_ODD_MULTIPLE_OF_FIVE,1,1) = '4'
)
等价地,也可以表达为“至少有一个字段首字符不是 4”。
substr(:P3_MULTIPLE_OF_TEN,1,1) != '4'
or substr(:P3_ODD_MULTIPLE_OF_FIVE,1,1) != '4'
为了支持翻译,可以定义名为 CANNOT_BOTH_START 的文本消息,并使用 first_digit 占位符。
Cannot both start with a %first_digit
验证的错误消息属性可以这样引用它:
&{CANNOT_BOTH_START first_digit="4"}.
20.1.2.2 通过返回错误文本做验证#
类型为 Function Body (returning Error Text) 的验证使用返回值表达结果:返回 null 表示通过;返回其他文本表示失败,且该文本就是要显示的错误消息。
下面示例检查两个数字字段是否至少有一个大于 100。如果都小于 100,则通过 APEX_LANG.GET_MESSAGE 返回可翻译错误消息。
return case
when :P3_MULTIPLE_OF_TEN < 100
and :P3_ODD_MULTIPLE_OF_FIVE < 100 then
apex_lang.get_message(
'AT_LEAST_N_ABOVE_M',
apex_t_varchar2(
'mincount','1',
'limit','100'))
end;
这里的 CASE 没有 ELSE 分支,因此条件不满足时自然返回 null,验证通过。
20.1.3 自动关闭成功消息#
默认情况下,成功消息和错误消息会一直显示,直到用户关闭。也可以在应用的用户界面属性中启用 Auto Dismiss Success Messages,让成功消息在五秒后自动消失。
这个选项需要谨慎使用。自动消失可能影响可访问性,有些用户来不及读完消息。如果启用,最好配合 apex.message.setDismissPreferences JavaScript API,让用户可以选择是否自动关闭。
20.1.4 使用动态操作显示消息#
动态操作(Dynamic Action)可以在页面事件发生时显示成功消息或错误消息。原生动作 Show Success Message 和 Show Error Message 适合在不提交页面的情况下向用户反馈。
下面的示例在用户修改员工编号后立即做服务器端检查。包 MESSAGES_APP 中的 CHECK_EMPNO 函数如果找到合法员工编号就返回 null,否则返回错误消息。
-- In package messages_app
function check_empno(
p_empno in number)
return varchar2
is
begin
for j in (select null from emp where empno = p_empno) loop
return null; -- valid
end loop;
return apex_lang.get_message('INVALID_EMPNO'); -- invalid
end;
页面上准备一个隐藏页面项 P4_INVALID_MESSAGE,并关闭 Value Protected。动态操作 Eagerly Check Empno 使用 Execute Server-side Code 执行下面一行 PL/SQL,把检查结果写入隐藏项。
:P4_INVALID_MESSAGE := messages_app.check_empno(:P4_EMPNO);
该动作需要在 Items to Submit 中提交 P4_EMPNO,并在 Items to Return 中返回 P4_INVALID_MESSAGE,确保浏览器中的最新值传到服务器,服务器结果再回到页面。
接着添加 Show Error Message 动作,仅当 P4_INVALID_MESSAGE 不为空时执行。消息文本可以使用 &P4_INVALID_MESSAGE. 替换语法。
同一检查还应在提交时通过正式验证执行,避免只依赖客户端交互。示例中的 Check Valid Empno 验证使用 Function Body (returning Error Text) 类型调用同一个函数。
最终效果是用户离开 Employee ID 字段后立即看到无效员工编号的页面级错误,不必等到提交页面。
20.1.5 通过程序显示消息#
除了声明式属性,APEX 还提供服务器端和客户端 API。服务器端可以用 APEX_ERROR.ADD_ERROR 添加错误;客户端可以用 apex.message 显示或清除成功与错误消息。
官方来源:Showing Messages Programmatically
20.1.5.1 在 PL/SQL 中添加错误消息#
APEX_ERROR.ADD_ERROR 用于在服务器端处理中向用户报告错误。它会停止后续处理,放弃未提交的数据变更,并在通知区域显示错误。
这种方式特别适合放在保存数据之后、APEX 最终提交之前的页面进程中,用来检查跨行、聚合或业务级规则。
官方来源:Adding an Error Message in PL/SQL
20.1.5.1.1 利用读一致性执行规则检查
用户提交页面后,页面进程会先把变更写入数据库,但 APEX 引擎会等所有提交处理都成功后才提交事务。只要某个后续页面进程报告错误,就能否决整次提交,之前保存进程写入的变更也不会提交。
Oracle 的读一致性让后续 SQL 能看见当前事务里尚未提交的变更,因此可以在保存进程之后运行聚合查询,例如 SUM 或 COUNT,用来判断即将提交的数据是否违反业务规则。
20.1.5.1.2 用 SQL 聚合实现约束
假设业务规则要求每个部门的月度总薪酬不能超过 12000。下面的查询用 SUM、GROUP BY 和 HAVING 找出超限部门。COALESCE 把空工资或佣金按 0 处理。
-- Return any departments over monthly compensation limit
select deptno,
sum(coalesce(sal,0) + coalesce(comm,0)) as total_comp
from emp
group by deptno
having sum(coalesce(sal,0) + coalesce(comm,0)) > 12000
过程 CHECK_DEPARTMENTS_OVERBUDGET 遍历这些部门,把部门号和总薪酬加入字符串列表。如果列表不为空,就调用 APEX_ERROR.ADD_ERROR,显示通过 APEX_LANG.GET_MESSAGE 取得的可翻译消息。
-- In package messages_app
procedure check_departments_overbudget is
l_overbudget apex_t_varchar2;
begin
for j in (
select deptno,
sum(coalesce(sal,0) + coalesce(comm,0)) as total_comp
from emp
group by deptno
having sum(coalesce(sal,0) + coalesce(comm,0)) > 12000
order by deptno) loop
apex_string.push(
l_overbudget,
apex_string.format('%s (%s)',j.deptno,j.total_comp));
end loop;
if l_overbudget is not null then
apex_error.add_error (
p_message => apex_lang.get_message(
'OVERBUDGET_DEPARTMENTS',
apex_t_varchar2(
'department_list',apex_string.join(l_overbudget,', '),
'amount' ,'12000')),
p_display_location => apex_error.c_on_error_page);
end if;
end check_departments_overbudget;
这类检查不能放在普通验证中,因为验证运行在 Processing 阶段之前,看不到保存进程即将提交的数据。把它放在保存进程之后的页面进程中,既能看到待提交变更,又能通过错误否决事务。
20.1.5.2 使用 JavaScript 显示消息#
客户端逻辑可以调用 apex.message.showPageSuccess 显示成功消息,调用 apex.message.showErrors 显示一个或多个页面级或字段级错误,并用 apex.message.clearErrors 清除所有错误或某个页面项的错误。
下面示例补充一个 CHECK_DEPTNO 函数,用于即时验证部门编号。
-- In package messages_app
function check_deptno(
p_deptno in number)
return varchar2
is
begin
for j in (select null from dept where deptno = p_deptno) loop
return null; -- valid
end loop;
return apex_lang.get_message('INVALID_DEPTNO'); -- invalid
end;
目标是在用户提交页面前,同时对 P5_EMPNO 与 P5_DEPTNO 做服务器端检查,并把错误显示在对应字段下方。
在 P5_EMPNO 的 Change 事件动态操作中,先执行 JavaScript 清除该字段已有错误。
apex.message.clearErrors('P5_EMPNO');
然后 Execute Server-side Code 动作调用服务器端函数,把返回的错误消息写入隐藏页面项 P5_INVALID_MESSAGE。
:P5_INVALID_MESSAGE := messages_app.check_empno(:P5_EMPNO);
最后用 showErrors 把服务器返回的消息显示在 P5_EMPNO 字段附近。该动作应配置客户端条件,仅在 P5_INVALID_MESSAGE 不为空时执行。
apex.message.showErrors(
[
{
type: "error",
location: [ "inline" ],
pageItem: "P5_EMPNO",
message: $v('P5_INVALID_MESSAGE')
}
] );
如果错误消息文本包含 HTML 标记,可向 showErrors 传入额外属性 unsafe: false,避免尖括号等保留字符被转义。
验证 P5_DEPTNO 时复用同一模式:提交部门编号,调用 CHECK_DEPTNO,把错误放到同一个隐藏项,再把消息显示在 P5_DEPTNO 字段上。
20.1.6 自定义错误消息显示#
错误处理函数(Error Handling Function)可以在 APEX 把服务器端错误报告给用户前介入,适合把数据库约束名、触发器异常或内部错误转换成更友好的消息。
官方来源:Customizing Error Message Display
20.1.6.1 观察默认错误行为#
为了理解自定义错误处理的价值,可以先看默认行为。假设在 EMP 表上增加约束,要求佣金不能超过工资的两倍。
alter table emp add constraint comm_less_than_twice_sal
check (comm < 2 * sal);
默认情况下,违反该约束时,用户会看到包含约束名的数据库错误,例如 ORA-02290: check constraint (SCHEMA.COMM_LESS_THAN_TWICE_SAL) violated。这对开发者有用,但对普通用户不够友好。
APEX 引擎内部错误也类似。默认消息可能说明技术原因,并提示联系应用管理员,例如表单区域没有定义主键项。
20.1.6.2 指定错误处理函数#
APEX 引擎会在报告服务器端错误前调用错误处理函数。函数名称可以自定义,但签名必须接收 apex_error.t_error,并返回 apex_error.t_error_result。
-- In package messages_app
function apex_error_handling (
p_error in apex_error.t_error )
return apex_error.t_error_result;
实际项目中通常把函数放在应用包中,例如 MESSAGES_APP。然后在页面属性的 Error Handling Function 中填写函数名。
20.1.6.3 阅读错误处理函数代码#
典型错误处理函数会先调用 apex_error.init_error_result 初始化返回结果。对于少见的内部错误,它记录日志,并把用户看到的消息替换成通用文本消息。对于数据库约束错误,它提取约束名,再尝试查找对应的可翻译文本消息。
-- In package messages_app
function apex_error_handling (
p_error in apex_error.t_error )
return apex_error.t_error_result
is
l_result apex_error.t_error_result;
l_constraint_name varchar2(255);
l_error_message varchar2(255);
begin
l_result := apex_error.init_error_result(p_error);
if p_error.is_internal_error then
if not p_error.is_common_runtime_error then
add_error_log(p_error);
l_result.message := apex_lang.get_message('UNEXPECTED_INTERNAL_ERROR');
l_result.additional_info := null;
end if;
else
l_result.display_location := apex_error.c_inline_in_notification;
if p_error.ora_sqlcode in (-1, -2091, -2290, -2291, -2292) then
l_constraint_name := apex_error.extract_constraint_name(
p_error => p_error );
begin
l_error_message :=
apex_lang.get_message('CONSTRAINT_' || l_constraint_name);
if l_error_message != 'CONSTRAINT_' || l_constraint_name then
l_result.message := l_error_message;
end if;
exception
when no_data_found then
null;
end;
end if;
if p_error.ora_sqlcode is not null
and l_result.message = p_error.message then
l_result.message := apex_error.get_first_ora_error_text(
p_error => p_error );
end if;
end if;
return l_result;
end apex_error_handling;
这里的约束消息约定是:如果约束名为 XXX,则查找文本消息 CONSTRAINT_XXX。如果找到了真正的文本,就把它作为用户消息;如果没有,就退回到简化后的 ORA 错误文本。
20.1.6.4 记录错误信息用于排查#
辅助过程 ADD_ERROR_LOG 接收 APEX 传入的 T_ERROR 记录,把错误上下文写入自定义日志表。日志表可以保存应用、页面、用户、浏览器、IP、页面项、区域、列、行号、APEX 错误码、ORA 错误码、调用栈等信息。
create table messages_app_errors (
id number generated by default on null as identity not null,
err_time timestamp(6) default systimestamp,
app_id number,
app_page_id number,
app_user varchar2(512),
user_agent varchar2(4000),
ip_address varchar2(512),
ip_address2 varchar2(512),
message varchar2(4000),
page_item_name varchar2(255),
region_id number,
column_alias varchar2(255),
row_num number,
apex_error_code varchar2(255),
ora_sqlcode number,
ora_sqlerrm varchar2(4000),
error_backtrace varchar2(4000),
procedure_name varchar2(1000),
error_text varchar2(4000),
constraint message_app_errors_id_pk primary key (id)
);
日志过程使用自治事务(pragma autonomous_transaction),并在插入日志后显式 commit。这样即使 APEX 因错误回滚业务数据,排查日志也能保留下来。
-- In package messages_app
procedure add_error_log (
p_error in apex_error.t_error,
p_procedure_name in varchar2 default null,
p_error_text in varchar2 default null)
is
pragma autonomous_transaction;
begin
delete from messages_app_errors
where err_time <= current_timestamp - 21;
insert into messages_app_errors (
app_id, app_page_id, app_user, user_agent,
ip_address, ip_address2, message, page_item_name,
region_id, column_alias, row_num, apex_error_code,
ora_sqlcode, ora_sqlerrm, error_backtrace,
procedure_name, error_text)
select v('APP_ID'),
v('APP_PAGE_ID'),
v('APP_USER'),
owa_util.get_cgi_env('HTTP_USER_AGENT'),
owa_util.get_cgi_env('REMOTE_ADDR'),
sys_context('USERENV', 'IP_ADDRESS'),
substr(p_error.message,0,4000),
p_error.page_item_name,
p_error.region_id,
p_error.column_alias,
p_error.row_num,
p_error.apex_error_code,
p_error.ora_sqlcode,
substr(p_error.ora_sqlerrm,0,4000),
substr(p_error.error_backtrace,0,4000),
p_procedure_name,
p_error_text
from dual;
commit;
end add_error_log;
完整字段可参考 APEX_ERROR 包文档中 T_ERROR 记录的说明。
20.1.6.5 体验自定义错误处理#
配置错误处理函数后,可以通过约束违反、数据库异常和内部错误来验证效果。下面的触发器故意制造两种数据库错误:当 COMM 为 1234 时触发除零错误;当 COMM 为 1235 时抛出 RAISE_APPLICATION_ERROR。
create or replace trigger emp_conditional_error_test
before insert or update on emp
for each row
begin
if :new.comm = 1234 then
if :new.comm / 0 > 1 then
null;
end if;
elsif :new.comm = 1235 then
raise_application_error(-20020,'Some error from the database');
end if;
end;
当用户输入的佣金超过工资两倍时,错误处理函数可以把约束错误转换为文本消息 CONSTRAINT_COMM_LESS_THAN_TWICE_SAL 对应的友好文案。
当用户输入 1235 时,页面显示的是简化后的数据库异常文本,而不是完整技术堆栈。
当用户输入 1234 造成除零错误时,用户也只看到简化后的错误,例如 divisor is equal to zero。
对于少见的内部 APEX 错误,错误处理函数可以向用户显示通用消息,同时把详细信息记录到日志表。例如紧急修复时误关表单主键项的 Primary Key 开关,生产用户可能只看到“发生了意外的内部应用错误”。
管理员可以查看只对管理员开放的日志页面,定位错误发生在哪个应用和页面。
进入详情后,可以看到错误处理函数记录的更多上下文,从而快速修复并重新发布应用。
20.2 使用模板发送电子邮件#
APEX 可以通过原生 Send E-Mail 页面进程或工作流活动发送邮件。任务定义和自动化中的原生动作类型也遵循同样模式。需要在业务逻辑中发邮件时,可以使用 APEX_MAIL 包的 SEND 过程或函数。
邮件发送通常进入队列。可以通过 APEX_MAIL_QUEUE 查看待发送邮件,通过 APEX_MAIL_LOG 查看近期已发送邮件。
官方来源:Sending Emails with Templates
20.2.1 设置默认发件人地址#
原生 Send E-Mail 功能默认使用 &APP_MAIL. 作为 From 属性。这个值来自应用定义 Properties 区域中的 Application Email From Address。
APEX 实例管理员负责配置供所有工作区使用的邮件服务器设置,普通应用开发者通常只需要配置应用自身的默认发件人地址。
20.2.2 不使用模板发送电子邮件#
一次性邮件可以直接在页面进程中写纯文本或 HTML 正文,并使用替换字符串填入页面数据。示例中的 Welcome Packet 邮件使用原生 Send E-Mail 页面进程,收件人来自 &P34_EMPLOYEE_EMAIL.,同时配置主题、纯文本正文和用于附加 ZIP 文件的 SQL。
20.2.3 定义带指令的电子邮件模板#
电子邮件模板把格式与每次发送时变化的数据、附件分离。发送时引用现有模板,设置占位符值,并按需提供附件即可。
模板的 Static Identifier 很重要,PL/SQL API 通过它引用模板。模板正文可以使用 HTML Template Directives,根据占位符是否有值决定是否输出某段内容。
示例模板包含 EMPLOYEE_NAME、NEW_SALARY 和 NEW_COMMISSION 占位符。只有 NEW_COMMISSION 有值时,模板才通过 {if/} 与 {endif/} 输出新佣金短语。
{if NEW_COMMISSION /}
, and your commission is now #NEW_COMMISSION#
{endif/}
20.2.4 使用电子邮件模板和占位符#
原生 Send E-Mail 动作、页面进程或工作流活动使用邮件模板时,需要为当前邮件设置占位符值。占位符值可以来自列、页面项、工作流变量、应用项或系统替换值,具体取决于当前上下文。
示例自动化的 Send Email 动作使用 Employee Salary Lottery Winner 模板,并把三项占位符映射到当前自动化行的列值:EMPLOYEE_NAME 对应 &ENAME.,NEW_SALARY 对应 &NEW_SALARY.,NEW_COMMISSION 对应 &NEW_COMMISSION.。
20.2.5 从业务逻辑发送电子邮件#
如果需要从 PL/SQL 业务逻辑发送同一封模板邮件,可以调用 APEX_MAIL.SEND。未传 p_from 时,它默认使用 APP_EMAIL 的值。p_placeholders 参数需要 JSON 文档;APEX_STRING.PLIST_TO_JSON_CLOB 可以把名称和值的字符串列表转换成 JSON。
apex_mail.send(
p_to => :EMPLOYEE_EMAIL,
p_template_static_id => 'EMPLOYEE_SALARY_LOTTERY_WINNER',
p_placeholders => apex_string.plist_to_json_clob(
apex_t_varchar2(
'EMPLOYEE_NAME' , :ENAME,
'NEW_SALARY' , :NEW_SALARY,
'NEW_COMMISSION', :NEW_COMMISSION)));
字符串列表中的奇数位置是 JSON 属性名,偶数位置是对应值。
20.3 发送推送通知#
APEX 可以通过原生 Send Push Notification 页面进程或工作流活动向已选择接收通知的用户发送推送通知。任务定义和自动化中的原生动作类型也按同样方式工作。
从业务逻辑发送推送通知时,使用 APEX_PWA.SEND_PUSH_NOTIFICATION。待发送推送通知可以通过 APEX_PUSH_NOTIFICATIONS_QUEUE 查看。
官方来源:Delivering Push Notifications
20.3.1 在应用中启用推送通知#
要启用推送通知,在 Shared Components 的 Progressive Web App 区域中打开 Enable Progressive Web App 和 Enable Push Notifications 两个开关。
某些移动平台要求推送通知与已安装的 Progressive Web App (PWA) 关联,因此通常也应考虑启用 Installable,让用户可以一键或轻触安装应用。安装后,应用会像原生应用一样出现在桌面或移动设备上。
20.3.2 为推送通知生成密钥对#
推送通知使用公钥/私钥凭据进行加密和签名。点击 Generate Credentials 可以生成新的 Key Pair 凭据,并将其选为推送通知使用的 Credentials。
生成凭据后,该按钮会变成 Regenerate Credentials。把应用部署到新环境,或需要更换推送通知签名密钥时,需要重新生成凭据。更换密钥后,用户通常需要重新选择接收通知。
20.3.3 让用户管理通知设置#
推送通知只会发送给已经选择接收通知的用户。为了让用户自行管理偏好,可以在 Progressive Web App 应用设置页的 Push Notifications 区域点击 Add Settings Page,让 APEX 添加设置页面和菜单入口。
20.3.4 选择接收推送通知#
最终用户在每台设备上从已登录用户菜单进入设置,选择接收推送通知。例如用户 Lucy 打开 Settings,进入 Push Notifications,启用 Enable push notifications on this device,然后在系统提示中允许通知。
这个用户偏好会持续保存,直到用户关闭。如果应用更换了 Key Pair 凭据,用户需要重新选择接收通知。
20.3.5 给同事推送一条笑话#
推送通知常用于重要事件,也可以传递轻量消息。示例页面 Send Chuckle 让用户选择一位同事,并把随机生成的一句话作为推送通知发给对方。
页面先使用名为 Get Random Chuckle 的 Invoke API 页面进程调用 RANDOM_CHUCKLE 函数,把结果放入隐藏页面项 P11_RANDOM_CHUCKLE。随后 Send Push Notification 页面进程把通知发给 P11_USERNAME 选择列表中的用户名。
APEX 会自动把通知发送给该用户已订阅的设备。若目标用户没有选择接收通知,APEX 会直接忽略该用户,不需要开发者自己维护订阅列表。
已选择接收推送的 Lucy 会在手机和配对手表上收到这条消息。
20.3.6 覆盖通知目标链接#
默认情况下,用户点击或轻触推送通知会打开应用首页。也可以配置 Target Link,让通知跳转到应用中的特定页面。
要让目标链接按预期工作,应使用绝对 URL,并在应用 Security 设置的 Session Management 区域启用 Deep Linking。
20.3.7 从业务逻辑发送推送通知#
如果要从 PL/SQL 业务逻辑发送同样的推送通知,可以调用 APEX_PWA.SEND_PUSH_NOTIFICATION。可选参数 p_target_url 与 p_icon_url 可用于覆盖默认目标链接和图标。
apex_pwa.send_push_notification(
p_user_name => :P11_USERNAME,
p_title => 'Need a Chuckle?',
p_body => :P11_RANDOM_CHUCKLE);