NotificationHelper
NotificationHelper 提供消息通知和待办事项的管理能力,包括基于消息模板的多渠道通知发送(站内消息 / 待办 / 邮件 / 短信)、手动发送待办、以及待办的关闭与删除。
概述
在业务流程中,及时的消息通知和待办提醒是提升用户体验的关键。NotificationHelper 封装了平台的消息通知能力:
- 模板化通知(
NotifyAsync):传入消息模板 ID 和接收人,服务端根据模板中勾选的渠道(站内消息、待办、邮件、短信)自动分发。模板勾选了待办渠道时,一次调用即可同时发出待办,无需再单独调用SendTodoAsync。 - 手动待办(
SendTodoAsync):不依赖消息模板,直接向用户写入待办事项,适合临时性、不想维护模板的场景。 - 待办管理(
CloseTodoAsync/BatchCloseTodoAsync/BatchDeleteTodoAsync):按业务数据 ID 等条件关闭或删除待办。
消息通知(Message Notification / 站内信 / 消息模板)与待办(Todo / To-do)的配置入口参见消息通知模块。
API 方法
NotifyAsync — 模板化通知发送
向一个或多个用户发送模板通知。服务端根据模板配置的渠道自动分发到站内消息、待办、邮件、短信。
Task NotifyAsync(params NotificationRequest[] dataList);
必填校验(不满足抛出 ChameleonException):
| 字段 | 校验 |
|---|---|
TemplateId | 必填,且必须为合法 GUID |
Receiver | 必填,且必须为合法 GUID |
示例:
// 发送单条模板通知,渠道由模板配置决定
await sdk.NotificationHelper.NotifyAsync(new NotificationRequest
{
TemplateId = "template-guid",
Receiver = "receiver-user-id",
Parameters = new Dictionary<string, string>
{
{ "UserName", "张三" }
},
BizDataId = context.CurrentEntity.Id, // 可选,关联业务数据
MetaObjectCode = "LeaveRequest", // 可选,优先使用;未传时用 MetaObjectId
Scene = "LeaveApproval" // 可选,待办场景标识
});
// 批量发送
await sdk.NotificationHelper.NotifyAsync(
new NotificationRequest { TemplateId = "template-guid", Receiver = "user-1" },
new NotificationRequest { TemplateId = "template-guid", Receiver = "user-2" }
);
NotifyAsync 的渠道分发机制
服务端读取消息模板的渠道配置,勾选了哪些渠道就发哪些,模板未勾选的渠道不会发送:
| 模板渠道配置 | 渠道 | 模板内容字段 |
|---|---|---|
SystemMessageEnable | 站内消息 | SystemMessageSubject / SystemMessageContent |
SystemTodoEnable | 待办 | SystemTodoSubject / SystemTodoContent |
EmailEnable | 邮件 | EmailSubject / EmailContent |
SmsEnable | 短信 | SmsContent |
模板内容中的占位符会被替换后发送:
{参数名}:替换为NotificationRequest.Parameters中同名参数的值{Model.字段编码}:替换为BizDataId对应业务数据中该字段的值(需同时传入MetaObjectCode或MetaObjectId用于定位对象,优先使用MetaObjectCode)
如果消息模板勾选了「待办」渠道(SystemTodoEnable = true),调用一次 NotifyAsync 即可同时发出待办,服务端会自动完成待办落库,不需要再单独调用 SendTodoAsync。
只有在不想使用消息模板、希望手动指定标题和内容直接发送待办时,才需要使用 SendTodoAsync。
SendTodoAsync — 手动发送待办
不使用消息模板,直接向用户发送待办事项,写入 system.todo 集合。
Task SendTodoAsync(params MessageNotificationRequest[] datas);
必填校验:Receiver 必填且必须为合法 GUID;Sender 不传时默认为当前登录用户。
示例:
// 手动发送待办,不依赖消息模板
await sdk.NotificationHelper.SendTodoAsync(new MessageNotificationRequest
{
Receiver = "approver-user-id",
Subject = "审批待办:请假申请",
Content = "张三提交了请假申请,请及时审批",
BizDataId = context.CurrentEntity.Id,
Scene = "LeaveApproval"
});
CloseTodoAsync — 关闭待办
关闭指定的待办事项(将 ishandled 置为 true,不删除记录)。
Task CloseTodoAsync(string bizDataId, string scene = null, string reciever = null);
参数说明:
| 参数 | 说明 |
|---|---|
| bizDataId | 关联的业务数据 ID,必填 |
| scene | 待办场景标识(可选),用于区分同一数据在不同场景下的待办 |
| reciever | 待办接收人(可选),不传则关闭该数据的所有待办 |
示例:
// 审批完成后关闭待办
await sdk.NotificationHelper.CloseTodoAsync(bizDataId: context.CurrentEntity.Id);
BatchCloseTodoAsync — 批量关闭待办
批量关闭待办事项,支持两种重载:
// 按 bizDataIds 批量关闭
Task BatchCloseTodoAsync(IEnumerable<string> bizDataIds, string scene = null, string reciever = null);
// 按 bizDataId + receivers 批量关闭
Task BatchCloseTodoAsync(string bizDataId, string scene, IEnumerable<string> recievers);
示例:
// 批量关闭多个数据的待办
await sdk.NotificationHelper.BatchCloseTodoAsync(
bizDataIds: new[] { "data-1", "data-2", "data-3" },
scene: "BatchApproval"
);
BatchDeleteTodoAsync — 批量删除待办
批量删除待办事 项(从 system.todo 集合中彻底移除),重载形式与 BatchCloseTodoAsync 相同:
// 按 bizDataIds 批量删除
Task BatchDeleteTodoAsync(IEnumerable<string> bizDataIds, string scene = null, string reciever = null);
// 按 bizDataId + receivers 批量删除
Task BatchDeleteTodoAsync(string bizDataId, string scene, IEnumerable<string> recievers);
典型场景
模板勾选待 办渠道:一次调用发送通知 + 待办
public async Task<object> ExecuteAsync(ComponentContext context, ChameleonSDK sdk)
{
// 模板勾选了「待办」渠道时,待办随本次调用自动发出
// 模板中的待办内容通过 {参数名} 或 {Model.字段编码} 占位符动态填充
await sdk.NotificationHelper.NotifyAsync(new NotificationRequest
{
TemplateId = "message-template-id", // 已勾选待办渠道的消息模板
Receiver = "approver-id",
Parameters = new Dictionary<string, string>
{
{ "DocName", context.MetaObject.Name }
},
BizDataId = context.CurrentEntity.Id,
MetaObjectCode = context.MetaObject.Code, // 也可传 MetaObjectId
Scene = "MyApproval"
});
return new { success = true };
}
不使用模板:手动发送待办并抄送通知
需要两个不同模板(待办模板 + 抄送通知模板)分别调用:
public async Task<object> ExecuteAsync(ComponentContext context, ChameleonSDK sdk)
{
var bizDataId = context.CurrentEntity.Id;
// 1. 手动发送待办给审批人(不走模板)
await sdk.NotificationHelper.SendTodoAsync(new MessageNotificationRequest
{
Receiver = "approver-id",
Subject = "新的审批待办",
Content = $"有一条{context.MetaObject.Name}等待您审批",
BizDataId = bizDataId,
Scene = "MyApproval"
});
// 2. 模板化抄送通知给相关人员
await sdk.NotificationHelper.NotifyAsync(new NotificationRequest
{
TemplateId = "cc-notify-template-id",
Receiver = "cc-user-id"
});
return new { success = true };
}
业务完成后清理待办
public async Task<object> ExecuteAsync(ComponentContext context, ChameleonSDK sdk)
{
// 处理完业务逻辑后...
// 关闭相关的待办
await sdk.NotificationHelper.CloseTodoAsync(
bizDataId: context.CurrentEntity.Id,
scene: "MyBusinessScene"
);
return new { success = true };
}
属性明细
以下内容面向技术实现与 AI 检索。
NotificationRequest 属性表
模板化通知请求,发送入口 NotificationOpenApiController(POST api/OpenApi/Notification/Notify)。
| 属性名 | 类型 | 含义 | 取值/存储值 |
|---|---|---|---|
TemplateId | string | 消息模板 ID | 必填,GUID,对应 system_metamessagetemplate |
Parameters | IDictionary<string, string> | 模板占位符参数 | 可空,替换模板内容中的 {参数名} |
Receiver | string | 接收人用户 ID | 必填,GUID |
Sender | string | 发送者 | 可空,服务端不强制填充 |
SentTime | DateTime | 发送时间 | 默认 DateTime.UtcNow |
Link | string | 消息链接 | 可空,支持占位符替换 |
BizDataId | string | 业务数据 ID | 可空,用于关联业务数据与待办关闭 |
MetaObjectId | string | 对象 ID | 可空,GUID;未传 MetaObjectCode 时用于 定位对象 |
MetaObjectCode | string | 对象编码 | 可空,优先使用;与 MetaObjectId 二选一即可启用 {Model.字段编码} 占位符 |
Scene | string | 待办场景 | 可空,建议填写 |
IsHandled | bool | 是否已处理 | 默认 false |
ExtendedParameters | Dictionary<string, string> | 额外参数 | 可空,JSON 序列化存储 |
MessageNotificationRequest 属性表
手动待办请求,SendTodoAsync 直接写入 system.todo 集合,键名与属性映射如下。
| 属性名 | 类型 | 含义 | 取值/存储值 |
|---|---|---|---|
Subject | string | 消息主题 | 存储键 title |
Content | string | 消息内容 | 存储键 content |
Receiver | string | 接收人用户 ID | 必填,GUID,存储键 receiver |
Sender | string | 发送者 | 可空,默认当前登录用户,存储键 sender |
SentTime | DateTime | 发送时间 | 默认 DateTime.UtcNow,存储键 senttime |
Link | string | 消息链接 | 可空,存储键 link |
BizDataId | string | 业 务数据 ID | 可空,存储键 bizdataid |
MetaObjectId | string | 对象 ID | 可空,存储键 metaobjectid |
MetaObjectCode | string | 对象编码 | 可空,存储键 metaobjectcode |
Scene | string | 待办场景 | 可空,建议填写,存储键 scene |
IsHandled | bool | 是否已处理 | 默认 false,存储键 ishandled |
ExtendedParameters | Dictionary<string, string> | 额外参数 | 可空,JSON 序列化后存储键 extendedparameters |
存储示例
待办数据(system.todo 集合):
{
"sender": "sender-user-id",
"receiver": "approver-user-id",
"senttime": "2026-11-04T08:00:00Z",
"title": "审批待办:请假申请",
"content": "张三提交了请假申请,请及时审批",
"link": null,
"bizdataid": "biz-data-id",
"metaobjectid": "meta-object-id",
"metaobjectcode": "LeaveRequest",
"scene": "LeaveApproval",
"ishandled": false,
"extendedparameters": null
}
服务端执行逻辑
NotifyAsync 的服务端执行入口为 NotificationService.NotifyAsync:
- 校验
TemplateId、Receiver均为合法 GUID,否则抛出ChameleonException - 按模板 ID 批量查询消息模板(
metaMessageTemplateRepository.GetListByIdsAsync) - 按
MetaObjectCode(优先)或MetaObjectId识别对象(ScopedCacheRepository.GetMetaObjectByIdOrCodeAsync,请求内缓存;对象不存在时抛出数据未找到异常),并拉取BizDataId对应的业务数据,用于{Model.字段编码}占位符替换 - 遍历模板与请求,按模板渠道开关(
SystemMessageEnable/SystemTodoEnable/EmailEnable/SmsEnable)分别组装各渠道消息 - 站内消息调
messageService.SendMessageAsync;待办调NotificationHelper.SendTodoAsync落库;邮件、短信异步发送(发送失败仅记日志,不抛出)
SendTodoAsync / CloseTodoAsync 等待办操作直接操作 system.todo 集合(dbContext.BatchAddAsync / BatchUpdateAsync / BatchDeleteAsync)。
异常与校验
| 场景 | 行为 |
|---|---|
TemplateId 为空或非 GUID | 抛出 ChameleonException("Template ID invalid") |
Receiver 为空或非 GUID | 抛出 ChameleonException("Receiver invalid") |
CloseTodoAsync/批量方法的 bizDataId(s) 为空 | 抛出参数校验异常(Ensure.ArgumentNotNullOrEmpty) |
| 邮件/短信发送失败 | 不抛出异常,仅记录 Error 日志 |
NotifyAsync 服务端异常 | 仅记录 Error 日志,不中断业务脚本 |
注意事项
- 优先使用模板化
NotifyAsync:模板勾选了待办渠道时一次调用即可完成通知 + 待办分发,无需重复调用SendTodoAsync SendTodoAsync发送的待办会在用户的待办中心显示,点击可跳转到关联的业务数据BizDataId通常传入当前业务数据的 ID,用于将待办与具体业务数据关联,也是后续CloseTodoAsync的定位条件Scene参数可用于区分同一业务数据在不同场景下的待办,关闭/删除时可通过scene精确匹配- 关闭待办不等于删除:关闭仅将
ishandled置为true,待办仍可在历史记录中查看;删除则是彻底移除 NotifyAsync的站内消息/待办为同步落库,邮件/短信为异步发送且失败不抛出异常