工作流里的传阅与抄送有什么区别?传阅任务要不要阻塞主流程?

在 OA 审批和工作流平台里,抄送与传阅都不会改变审批结论,但二者的业务承诺不同。抄送是把流程信息送达相关人,核心状态是“已发送、已查看”;传阅是要求指定人员阅读,核心状态是“待阅、已阅,必要时还要确认或填写阅办意见”。

传阅是否阻塞主流程,不能只根据按钮名称决定。默认情况下,普通传阅应采用非阻塞模式,让审批主线继续运行;只有当后续业务动作必须以指定人员完成阅读确认为前置条件时,才应把它建模为阻塞式签阅任务。换句话说:需要留痕,不等于必须等待;必须等待,也意味着它已经不再是纯通知。

本文以截至 2026 年 8 月最新正式版 Flowable 8.0.0 为技术基线,说明抄送、传阅、签阅的业务语义,分析 IdentityLink、历史任务、多实例用户任务的适用边界,并给出企业级数据模型、查询、幂等和权限设计。

一、核心结论与问题边界

先给答案:抄送是通知,传阅是阅读任务

判断一个功能究竟应该叫抄送还是传阅,先看系统向接收人作出了什么承诺。

能力 接收人要做什么 是否形成办理责任 典型状态 默认是否阻塞主流程
抄送 知悉信息,可选择查看 否 未读、已读 否
传阅 在期限内阅读,可确认或留阅办意见 轻量阅读责任 待阅、已阅、已确认、逾期 通常否
签阅 完成阅读确认后流程才能继续 是 待签阅、已签阅、退回补充 是
审批 作出同意、拒绝等业务决定 是 待审批、已同意、已拒绝 是

最稳妥的产品规则是:抄送永不生成审批待办;普通传阅进入“待阅”清单但不占用流程令牌;真正需要等待的传阅,在界面和审计中改称“签阅”或“阅读确认”。

在这里插入图片描述

图 1:抄送解决送达,传阅解决阅读留痕,签阅和审批才承担流程等待责任。

抄送是什么:让相关人知悉,而不是让他办理

抄送常见于审批通过后通知财务、人事、项目成员,或者在关键节点把进展同步给关注人。接收人通常可以查看单据、附件和流程轨迹,但不拥有同意、拒绝、退回等审批权限。

一条合格的抄送记录至少要回答五个问题:谁触发了抄送、在哪个流程节点触发、抄给了谁、当时允许查看哪些数据、接收人是否已经打开。它可以触发站内信、短信、邮件或企业即时通信通知,但消息发送结果不能替代业务抄送记录。

抄送不应进入“我的待办”,否则用户无法分辨哪些事项必须处理,待办 SLA 也会被大量通知污染。更合理的入口是“抄送我的”或消息中心,并允许按未读、已读、流程状态和抄送时间筛选。

抄送也不等于把接收人加入候选审批人。候选人能够 claim 任务并取得办理权,而抄送人只能阅读授权范围内的信息,二者在权限和审计上完全不同。

传阅是什么:要求阅读,但不一定要求流程等待

传阅常见于公文、制度、会议纪要、合同归档和重大事项知会。发起方不只希望“消息发出去”,还希望知道指定人员是否在期限内阅读,有时还要求填写简短阅办意见。

因此,传阅比抄送多了一个可核验的阅读生命周期:待阅、已打开、已确认、逾期、催阅、撤回。系统还可能保存首次打开时间、最终确认时间、确认渠道和意见,但不应把页面加载一次就自动解释为“已认真阅读”。

传阅可以存在两种责任强度:

  • 普通传阅:接收人应当阅读,逾期可提醒或统计,但主流程继续运行;
  • 阻塞式签阅:指定人数或指定岗位未确认前,后续节点不能开始。

前者适合信息传播,后者适合制度生效确认、上岗前安全告知、法务确认送达等强约束场景。两种模式必须在设计器和运行界面上显式区分。

二、关键概念与能力差异

为什么“已读”和“已完成”不能混为一谈

已读是阅读证据,已完成是流程推进动作。两者至少存在四个差异。

第一,已读通常没有审批结论,不应写入同意或拒绝变量。第二,已读可以在流程结束后补记,而审批任务通常随令牌推进而结束。第三,同一材料可能反复打开,但只需要一次确认记录。第四,抄送或普通传阅被撤回,不应该回滚已经完成的审批节点。

如果平台把“打开详情页”直接调用 taskService.complete,一次误触就可能推进主流程;如果把抄送记录伪装成候选任务,用户 claim 后又会获得不该有的办理权。正确做法是先定义领域状态,再决定是否需要 Flowable 用户任务。

传阅任务到底要不要阻塞主流程

可以用四个问题做判断:

  1. 接收人不阅读,后续业务是否合法或是否允许执行?
  2. 是否存在明确的截止时间、逾期升级和责任追究?
  3. 必须等待所有人、任意一人,还是达到一定比例?
  4. 接收人需要提交意见或附件,还是只需留下阅读确认?

如果第一个问题回答“否”,通常需要阻塞式签阅;如果只是希望统计触达率、阅读率和逾期率,应采用非阻塞传阅。不要因为管理者想看到“100% 已读”就轻易阻塞主流程,否则一名休假、离职或账号停用的人员就可能让整个流程永久停住。

在这里插入图片描述

图 2:是否阻塞取决于后续动作是否依赖阅读确认,而不是取决于功能名称。

三、模型架构与运行机制

三种落地模式应该怎样选择

1. 模式一:非阻塞抄送

流程在指定节点提交或结束时生成抄送记录和通知事件,主令牌立即继续。接收人打开详情后更新 readAt,不产生 Flowable 活动任务。

2. 模式二:非阻塞传阅

平台创建传阅批次和每人一条接收记录,状态从 PENDING 变为 OPENED、ACKNOWLEDGED 或 EXPIRED。主流程不等待,但后台可以催阅、统计、升级和追加传阅人。

3. 模式三:阻塞式签阅

在 BPMN 主线上建模用户任务或多实例用户任务。流程执行到该节点后产生等待状态,满足全部完成、任意一人完成或比例条件后才能继续。必须同时设计超时、替补人员、撤回和人员失效策略。

如果业务希望“审批先继续,但付款前必须全部阅毕”,可以采用延迟汇合:传阅本身非阻塞,主流程运行到付款前检查点时再校验阅读结果。它兼顾流程吞吐和关键控制,但一致性要求更高。

从 Flowable 看边界:用户任务天然会等待

Flowable 官方文档把 User Task 定义为需要人来完成的工作。执行到用户任务时,引擎创建任务并停留在等待状态,直到任务被完成。因此,只要把传阅直接画成主线用户任务,它就会阻塞主流程,这不是页面配置能够改变的显示效果,而是 BPMN 执行语义。

Flowable 8.0.0 的 TaskService 提供 claim、complete、addUserIdentityLink、getIdentityLinksForTask 等 API,也支持创建不关联流程实例的 standalone task。但这些能力并没有定义“中国式 OA 抄送”或“传阅回执”的完整语义。

IdentityLink 表示某个用户或用户组与任务有关联。官方内置类型包括 ASSIGNEE、CANDIDATE、OWNER、STARTER、PARTICIPANT 和 REACTIVATOR。应用也可以传入自定义关联类型,但 IdentityLink 只有关联身份、关联类型和创建时间等信息,没有未读、已读、确认、撤回和逾期字段。

因此,可以用 cc、circulation 等自定义 IdentityLink 辅助说明“谁与任务有关”,但不能把它当成抄送或传阅的权威业务表。

四、核心场景与处理策略

Flowable 8 的 TaskServiceImpl.addUserIdentityLink 会提交 AddIdentityLinkCmd。当类型不是 ASSIGNEE 或 OWNER 时,命令创建普通任务 IdentityLink,并交给历史管理器记录关联变化。任务结束后,HistoricIdentityLink 仍可保留当前关联状态。

taskService.addUserIdentityLink(taskId, userId, "cc");
List<IdentityLink> links = taskService.getIdentityLinksForTask(taskId);

这种方式的优点是关联靠近引擎任务,活动任务和历史任务都能追溯相关人员。局限也很明显:

  • taskInvolvedUser(userId) 会查询与用户存在任意 IdentityLink 的任务,并且包含 assignee 和 owner,不能直接等同于“抄送我的”;
  • IdentityLink 不保存通知渠道、阅读时间、撤回状态和数据权限快照;
  • 如果抄送在流程结束时才发生,可能已经没有合适的活动 taskId 可关联;
  • 一条业务单据可能跨多个任务抄送,同一个人容易重复出现。

因此,推荐把 IdentityLink 作为可选的引擎侧索引,业务侧仍建立独立 wf_copy_record,并以 copyId、processInstanceId、sourceTaskId 关联。

非阻塞抄送应该怎样实现

非阻塞抄送的核心是“业务记录与消息发送解耦”。推荐在完成审批事务时,同时写入抄送记录和 Outbox 事件;事务提交后,由消息消费者发送站内信、邮件或企业微信通知。

@Transactional
public String createCopy(CopyCommand cmd) {
    String copyId = copyRepository.insert(cmd.toRecord());
    outboxRepository.insert(CopyCreated.of(copyId, cmd.requestId()));
    return copyId;
}

消息发送失败只影响通知渠道,不应回滚已经成功的审批,也不应阻塞流程令牌。消费者必须以 copyId 或 requestId 幂等;如果需要多渠道发送,为每个渠道保存独立的 delivery 状态,不能用一个布尔值概括全部结果。

“已读”应在接收人通过授权打开抄送详情后写入。为了防止预览机器人、消息卡片预取或链接安全扫描误触,可要求前端详情页加载成功后调用专用确认接口,而不是把 HTTP GET 访问本身直接算作已读。

五、数据、规则与状态设计

非阻塞传阅应该怎样实现

非阻塞传阅建议使用“批次 + 接收人明细”模型。批次保存材料版本、发起人、触发节点、是否允许意见和截止时间;接收人明细保存 recipientId、status、openedAt、acknowledgedAt、opinion、reminderCount 和 version。

创建时先固化接收人快照,而不是每次查询都实时展开部门成员。否则部门人员变化后,历史传阅范围会漂移。若需要“传阅给部门且未来新成员也必须阅读”,应把它设计成动态订阅规则,而不是悄悄改变已发布批次。

接收人点击“确认已阅”时,以版本号做乐观锁更新,重复请求保持幂等。传阅人可以追加人员,但追加记录应保存 addedBy、addedAt 和 reason;撤回只阻止尚未阅读的人员继续访问,已经确认的记录不能物理删除。

在这里插入图片描述

图 3:主流程只发布领域事件,业务记录、通知投递和阅读确认在独立链路中完成。

阻塞式签阅怎样用 Flowable 建模

当主流程必须等待阅读确认时,应把它明确建模为用户任务。多人签阅可以使用并行或串行多实例:并行模式同时给所有人创建任务;串行模式按列表顺序逐人创建。

Flowable 多实例父执行保存 nrOfInstances、nrOfActiveInstances 和 nrOfCompletedInstances。默认所有实例完成后才离开,也可以用 completionCondition 表达任意一人、固定人数或比例完成。例如“至少 80% 完成”可以根据已完成数和总数计算。

但提前满足完成条件时,Flowable 会删除剩余活动实例并继续流程。产品必须把这些记录显示为“条件结束”或“无需继续”,不能误写成“已阅”。这也是为什么签阅任务状态和最终阅读统计不能只看流程是否已经离开节点。

在这里插入图片描述

图 4:多实例用户任务会占用流程等待状态,只有满足完成条件后主令牌才继续。

六、工程实现与系统集成

阻塞式签阅必须补齐哪些异常路径

一旦签阅阻塞主流程,就必须把异常路径当成流程设计的一部分:

  • 到期未签阅时是继续、升级、转交还是终止;
  • 接收人离职、停用或长期休假时由谁替补;
  • 追加签阅人是否改变总人数和完成比例;
  • 删除尚未完成的签阅人是否需要重新计算条件;
  • 并行实例提前结束后如何保留“未要求继续”的原因;
  • 主流程撤回、驳回或终止时,签阅任务怎样同步关闭。

不要设置一个没有退出策略的“全部人员必须已阅”。组织数据从来不是静态的,任何强等待都必须有计时器、管理员处置和审计原因。

为什么 standalone task 也不能直接等于传阅

Flowable TaskService.newTask() 可以创建不关联流程实例的独立任务,保存后也能进入任务查询。但它没有对应的 BPMN execution,完成它不会自动推进或阻塞某个主流程。

独立任务适合临时人工工作,却仍然缺少传阅批次、材料版本、阅读状态、撤回、通知投递和数据权限快照。若为了复用统一待办中心而创建 standalone task,平台仍需保存业务传阅表,并明确两者是一对一还是一对多关系。

对于非阻塞传阅,更简单的方案往往是让任务中心支持多种工作项类型:审批待办来自 Flowable,传阅待阅来自业务表,抄送来自通知视图。统一列表不代表统一存储,更不代表都必须伪装成引擎任务。

七、安全、性能与治理要求

数据表和查询入口应该怎样设计

推荐至少拆分以下三类数据:

数据对象 关键字段 主要用途
抄送记录 copyId、recipientId、sourceTaskId、readAt、status “抄送我的”与已读统计
传阅批次 batchId、materialVersion、mode、dueAt、policy 一次传阅的范围和规则
传阅接收人 batchId、recipientId、status、openedAt、ackAt、opinion “待阅/已阅”与回执审计
投递记录 bizId、channel、deliveryStatus、retryCount 消息渠道投递与重试

“我的待办”只查询需要办理并影响业务推进的任务;“待阅”查询未确认的传阅接收记录;“抄送我的”查询抄送记录。首页可以把三类数量汇总展示,但 API、权限和状态机应保持独立。

Flowable 的 taskInvolvedUser 和历史 IdentityLink 适合补充过程关联查询,不适合直接承担阅读回执。尤其是 taskInvolvedUser 会同时命中 assignee、owner 和其他 IdentityLink,应用必须按业务类型进一步过滤。

权限、材料版本和证据链怎样处理

抄送人或传阅人能查看哪些表单字段和附件,不能简单继承审批人的全部权限。创建记录时应固化权限策略或可见资源范围;打开时还要校验租户、账号状态、密级和记录是否被撤回。

对于制度、公文和合同,阅读证据必须绑定材料版本。文件被替换后,旧版本的“已阅”不能自动覆盖新版本。系统应根据版本变更类型决定保留旧回执、要求重新确认,或生成新的传阅批次。

合规场景还要保存确认渠道、客户端时间与服务端时间、设备或会话标识、材料摘要和操作流水。阅读时长只能作为辅助指标,不能单凭停留秒数证明用户理解了内容。

八、案例实践与迁移路线

一致性、幂等和性能容易踩哪些坑

审批事务内直接同步发送上百条消息,会拉长锁持有时间,也会让外部渠道故障拖垮流程提交。正确做法是事务内保存业务记录和 Outbox,事务后异步投递。

批量传阅应按接收人分页写入,并用 batchId + recipientId 建唯一约束。催阅任务按 dueAt 和 status 扫描,不能每次遍历全部历史记录。已读或确认接口使用幂等键和乐观锁,重复点击只返回同一结果。

搜索列表可以建立任务中心投影,但投影不是权威状态。用户点击详情或执行确认时必须回源校验;流程终止、材料撤回、人员停用等事件也要更新投影,避免出现“列表可见、打开无权”的长期脏数据。

设计器和任务中心应该怎样呈现

设计器不要只提供一个“抄送/传阅人员”选择框。至少应配置触发时点、人员规则、模式、截止时间、是否需要确认、是否允许意见、完成条件、逾期策略和数据权限。

运行时界面应使用清晰动词:抄送显示“查看”,普通传阅显示“确认已阅”,阻塞式签阅显示“完成签阅”。不要让用户在传阅页面看到同意、拒绝按钮,也不要把抄送未读数量混入审批逾期数量。
在这里插入图片描述

云程低代码开发平台可以在统一任务中心中聚合待办、待阅和抄送,但分别接入 Flowable 任务、传阅业务表和消息投影;设计器只声明业务语义,运行时由策略服务选择非阻塞事件或多实例用户任务。这样既保持统一体验,又不破坏各自的状态边界。

九、平台落地、测试与选型

上线前至少覆盖哪些测试

  1. 抄送生成后主流程立即继续,接收人没有审批按钮;
  2. 抄送通知失败可以重试,不回滚已经完成的流程事务;
  3. 页面预取和重复打开不会错误生成多条已读记录;
  4. 普通传阅逾期只触发提醒和统计,不阻塞主流程;
  5. 阻塞式签阅未满足完成条件时,后续节点不会创建;
  6. 多实例达到比例条件后,剩余任务标记为条件结束而不是已阅;
  7. 接收人离职、停用、请假和替补时存在可审计处置路径;
  8. 追加和移除传阅人会按策略重新计算人数与完成条件;
  9. 材料版本变更后能够要求重新确认;
  10. 流程撤回、终止和删除时,抄送、传阅和签阅状态正确联动;
  11. 用户只能查看授权字段和附件,跨租户访问被拒绝;
  12. Flowable 历史、业务回执、消息投递和任务中心投影可以对账。

测试不能只断言“某个人看到了记录”,还要验证流程令牌是否继续、阅读证据是否正确、未完成人员怎样收口,以及消息失败是否影响主事务。

标准答案:普通传阅默认不阻塞,强确认才建模为签阅

抄送的目标是让相关人知悉,接收人没有办理责任,默认不阻塞主流程。它应进入“抄送我的”或消息中心,而不是审批待办。

传阅的目标是取得可追踪的阅读结果。普通传阅使用独立的批次和接收人记录,支持待阅、已阅、催阅和意见,但主流程继续运行。只有当后续业务必须等待指定人员完成阅读确认时,才使用 Flowable 用户任务或多实例用户任务,并把它清晰命名为签阅或阅读确认。

Flowable IdentityLink 可以补充记录用户与任务的关联,HistoricIdentityLink 也能在任务结束后保留关联,但它不包含完整的阅读状态和证据链。企业平台仍需独立的抄送、传阅和投递数据模型。

如果只记住三句话:

  • 抄送解决“信息有没有送到”;
  • 传阅解决“指定人员有没有阅读”;
  • 阻塞式签阅解决“没有完成阅读确认,业务能不能继续”。
posted @ 2026-09-18 09:08  大龄码农有梦想  阅读(18)  评论(0)    收藏  举报