HarmonyOS 「星办OA」App应用实战58 : 第58篇:审批事件通知系统
第58篇:审批事件通知系统
概述
审批事件通知系统是连接审批操作和用户感知的桥梁。当审批状态发生变化时(如提交、同意、驳回、撤回),系统需要及时通知相关用户。星办OA 在 ApprovalStore.ets 中实现了完整的通知系统,包括待办转审批结果机制(resolvePendingMessages)、操作消息自动生成(addActionMessage)以及消息通知与审批操作的联动。本文将深入分析通知系统的实现细节。

一、通知系统架构
1.1 消息数据模型
export class ApprovalMessage {
id: string = '' // 消息唯一标识
category: string = '系统通知' // 消息分类(待办提醒/审批结果/系统通知)
title: string = '' // 消息标题
content: string = '' // 消息内容
approvalId: string = '' // 关联审批单号
createdAt: string = '' // 消息创建时间
isRead: boolean = false // 已读/未读状态
}
消息分类体系:
| 分类 | 标识 | 用途 | 示例 |
| 待办提醒 | '待办提醒' |
提醒用户有新的审批待处理 | "你有一笔报销待审批" |
| 审批结果 | '审批结果' |
通知用户审批操作结果 | "采购申请被驳回" |
| 系统通知 | '系统通知' |
系统级公告和提醒 | "移动办公安全提醒" |
1.2 通知系统的完整流程
审批操作 → Store 方法 → 数据更新 → 消息通知 → 用户感知
│ │ │ │
│ │ approvals 更新 │
│ │ messages 更新 │
│ │ │ │
approve() Store.approve() resolvePendingMessages() + addActionMessage() → Toast + 消息列表更新
二、待办转审批结果机制
2.1 核心函数实现
export function resolvePendingMessages(messages: ApprovalMessage[], approvalId: string,
action: string): ApprovalMessage[] {
let changed: boolean = false
let result: ApprovalMessage[] = []
messages.forEach((item: ApprovalMessage) => {
if (item.approvalId === approvalId && item.category === '待办提醒') {
// 找到匹配的待办提醒,转换为审批结果
let resolved: ApprovalMessage = cloneMessage(item)
resolved.category = '审批结果'
resolved.title = `待办已${action}`
resolved.content = `你已完成该申请的${action}操作。`
resolved.createdAt = '刚刚'
resolved.isRead = true
result.push(resolved)
changed = true
} else {
result.push(item)
}
})
return changed ? result : messages
}
2.2 待办转结果的业务逻辑
触发条件: 当审批人对一条待审批申请执行同意或驳回操作后,系统会查找与该审批关联的待办提醒消息。
转换规则:
| 字段 | 转换前 | 转换后 |
category |
"待办提醒" | "审批结果" |
title |
"你有一笔报销待审批" | "待办已同意" / "待办已驳回" |
content |
"周婷提交了..." | "你已完成该申请的同意操作。" |
createdAt |
原始时间 | "刚刚" |
isRead |
false |
true |
示例演示:
转换前(待办提醒):
消息ID: M001
分类: 待办提醒
标题: 你有一笔报销待审批
内容: 周婷提交了"客户拜访交通费报销"。
审批ID: A20260824001
已读: false
执行同意操作后,转换为此消息(审批结果):
消息ID: M001
分类: 审批结果
标题: 待办已同意
内容: 你已完成该申请的同意操作。
审批ID: A20260824001
已读: true
2.3 消息克隆函数
function cloneMessage(source: ApprovalMessage): ApprovalMessage {
return newMessage(source.id, source.category, source.title, source.content,
source.approvalId, source.createdAt, source.isRead)
}
消息克隆保证了原消息的不可变性,转换后的消息是新创建的对象,原消息不受影响。
三、操作消息自动生成
3.1 addActionMessage 实现
private addActionMessage(result: ApprovalMutation, title: string, content: string): void {
let item: ApprovalMessage = new ApprovalMessage()
item.id = `M${Date.now()}` // 基于时间戳生成唯一 ID
item.category = '审批结果' // 分类为审批结果
item.title = title // 消息标题
item.content = content // 消息内容
item.approvalId = result.approvalId // 关联审批单号
item.createdAt = '刚刚' // 创建时间
item.isRead = false // 未读
this.messages = [item, ...this.messages] // 插入到消息列表头部
}
3.2 各操作的消息生成
提交操作:
// 在 submit 方法中
if (result.success) {
this.approvals = result.approvals
this.addActionMessage(result, '申请已提交', `${type}申请已进入审批流程。`)
}
// 生成消息:{ title: "申请已提交", content: "报销申请已进入审批流程。" }
同意操作:
// 在 approve 方法中
if (result.success) {
this.approvals = result.approvals
this.messages = resolvePendingMessages(this.messages, id, result.action)
this.addActionMessage(result, '审批操作已完成', result.message)
}
// 生成消息:{ title: "审批操作已完成", content: "已同意,流程已转交下一审批人" }
// 或:{ title: "审批操作已完成", content: "审批已通过" }
驳回操作:
// 在 reject 方法中
if (result.success) {
this.approvals = result.approvals
this.messages = resolvePendingMessages(this.messages, id, result.action)
this.addActionMessage(result, '申请已驳回', comment.trim())
}
// 生成消息:{ title: "申请已驳回", content: "费用说明不完整" }
撤回操作:
// 在 withdraw 方法中
if (result.success) {
this.approvals = result.approvals
this.addActionMessage(result, '申请已撤回', '你提交的申请已成功撤回。')
}
// 生成消息:{ title: "申请已撤回", content: "你提交的申请已成功撤回。" }
四、消息通知与审批操作的联动
4.1 同步联动机制
在 ApprovalStore 中,每个审批操作都会同步触发消息通知:
approve(id: string, comment: string): ApprovalMutation {
let result: ApprovalMutation = approveApproval(this.approvals, id, comment, '刚刚')
if (result.success) {
this.approvals = result.approvals // 1. 更新审批数据
this.messages = resolvePendingMessages( // 2. 转换待办消息
this.messages, id, result.action)
this.addActionMessage(result, // 3. 生成操作消息
'审批操作已完成', result.message)
}
return result
}
三步联动机制:
- 更新审批数据:
this.approvals = result.approvals— 替换为最新的审批列表 - 转换待办消息:
resolvePendingMessages— 将关联的待办提醒转换为审批结果 - 生成操作消息:
addActionMessage— 在消息列表头部插入新的操作通知
4.2 消息 ID 的生成策略
item.id = `M${Date.now()}`
使用 Date.now() 时间戳作为消息 ID 的后缀,确保每次生成的消息 ID 都是唯一的。这种简单的 ID 生成策略在演示模式下足够使用,生产环境中可以使用更复杂的 UUID 生成策略。
4.3 消息列表的插入策略
this.messages = [item, ...this.messages]
新消息插入到消息列表的头部(...this.messages 展开语法),确保最新的消息始终显示在最前面。这是消息列表常见的"时间倒序"展示模式。
五、消息状态管理
5.1 已读/未读管理
markMessageRead(id: string): void {
let changed: boolean = false
this.messages.forEach((item: ApprovalMessage) => {
if (item.id === id && !item.isRead) {
item.isRead = true
changed = true
}
})
if (changed) {
this.messages = [...this.messages] // 触发视图更新
}
}
markAllMessagesRead(): void {
this.messages.forEach((item: ApprovalMessage) => {
item.isRead = true
})
this.messages = [...this.messages] // 触发视图更新
}
已读状态管理的设计要点:
- 变更检测:通过
changed标志位记录是否有消息被标记为已读,只有在有变更时才触发this.messages = [...this.messages]的组件更新。 - 数组展开:
[...this.messages]创建新的数组引用,触发@ObservedV2/@Trace的变更检测机制。 - 批量操作:
markAllMessagesRead支持一键标记所有消息为已读。
5.2 未读统计
getDashboardStats(approvals, messages) {
stats.unread = 0
messages.forEach((item: ApprovalMessage) => {
if (!item.isRead) {
stats.unread++
}
})
return stats
}
未读消息数通过 getDashboardStats 计算,在仪表盘和消息角标中展示,提醒用户有未读的通知。
六、通知系统的演示数据
6.1 初始消息数据
export function createDemoMessages(): ApprovalMessage[] {
return [
newMessage('M001', '待办提醒', '你有一笔报销待审批',
'周婷提交了"客户拜访交通费报销"。', 'A20260824001', '09:20', false),
newMessage('M002', '待办提醒', '你有一条请假申请待审批',
'陈昊申请 8 月 26 日至 27 日年假。', 'A20260823007', '昨天 16:40', false),
newMessage('M003', '审批结果', '采购申请被驳回',
'产品部显示器采购需要补充询价记录。', 'A20260821005', '8月21日', false),
newMessage('M004', '审批结果', '报销申请已通过',
'团队午餐费用已完成审批。', 'A20260818009', '8月19日', true),
newMessage('M005', '系统通知', '移动办公安全提醒',
'请勿将审批截图和企业数据转发到外部平台。', '', '8月18日', true),
]
}
6.2 消息与审批的关联
| 消息ID | 分类 | 关联审批 | 审批状态 | 是否可转换 |
| M001 | 待办提醒 | A20260824001(报销) | 审批中 | 是(同意/驳回后) |
| M002 | 待办提醒 | A20260823007(请假) | 审批中 | 是(同意/驳回后) |
| M003 | 审批结果 | A20260821005(采购) | 已驳回 | 否 |
| M004 | 审批结果 | A20260818009(团队午餐) | 已通过 | 否 |
| M005 | 系统通知 | 无 | - | 否 |
七、实际应用场景
7.1 场景一:审批人处理待办
- 审批人打开应用,看到消息列表中有 2 条未读的"待办提醒"
- 点击消息跳转到对应审批详情页
- 执行同意操作
- 系统自动将"待办提醒"消息转换为"审批结果"消息
- 同时生成一条新的"审批操作已完成"消息
- 审批人收到 Toast 提示"已同意,流程已转交下一审批人"
7.2 场景二:申请人收到通知
- 申请人提交了一个新的审批申请
- 系统自动生成一条"审批结果"类的消息
- 消息内容为"请假申请已进入审批流程。"
- 申请人在消息列表中看到这条新消息
八、总结
星办OA 的审批事件通知系统通过 ApprovalStore.ets 中的 resolvePendingMessages 和 addActionMessage 两个核心机制,实现了审批操作与消息通知的自动联动。
通知系统的核心能力:
- 待办转结果:审批人处理待办后,原待办提醒自动转换为审批结果,保持消息列表的整洁和准确。
- 操作自动通知:每次审批操作(提交、同意、驳回、撤回)都自动生成对应的通知消息。
- 已读状态管理:支持单条标记已读和批量标记已读,状态变更通过数组引用变更触发视图更新。
- 消息分类体系:三类消息(待办提醒、审批结果、系统通知)覆盖了审批系统的所有通知场景。
设计模式总结:
- 不可变更新:
resolvePendingMessages通过克隆创建新消息对象,不修改原消息。 - 变更检测优化:通过
changed标志位避免不必要的数组更新。 - 纯函数领域逻辑:
resolvePendingMessages是纯函数,易于测试和调试。 - Store 层协调:
ApprovalStore负责协调审批操作和消息通知的联动逻辑。
通过这套通知系统,星办OA 实现了审批操作与消息通知的闭环——每次操作都有对应的通知,每个通知都关联到具体的审批单,用户可以通过消息列表快速感知和响应审批状态的变化。
源文件路径: commons/common/src/main/ets/model/ApprovalStore.ets
更多推荐


所有评论(0)