日期: 2026-07-30
范围:
web-admin 审批设计器「3 流程设计」· 抄送人节点配置(FlowDesigner.vue / flow-tree/types.ts)backend 发布校验(oa_publish.py)、审批人解析(oa_assignee.py)、引擎建任务 / 预览 / 进度(oa_engine.py)OaFlowPreview 等相关前端)关联:
docs/superpowers/specs/2026-07-14-oa-approval-dingtalk-design.md §5.3(选人策略)docs/superpowers/specs/2026-07-23-oa-initiator-select-in-flow-design.md(抄送自选可空)approverConfig.assigneeType(单值),设计器为分段单选。users | oa_roles | dept_leader | direct_supervisor | initiator_select。resolve_assignees 按单一类型硬分支;失败则抛 AssigneeResolveError。assigneeType === initiator_select 且发起人未选人时,跳过建任务且不报错。users / oa_roles 则必须已配置成员/角色。assigneeType 的抄送配置,行为与现网一致。| 项 | 结论 |
|---|---|
| 范围 | 仅抄送人;审批人保持单选 |
| 运行时合并 | 各勾选方式独立解析 → 人 ID 并集去重(保序:按 assigneeTypes 顺序,同策略内保持原序) |
| 单策略失败/空 | 软跳过该策略,继续其它策略 |
| 最终 0 人 | 不报错,不创建抄送任务,流程继续 |
| 发布校验 | 必须勾选 ≥1 种;users / oa_roles 若勾选则配置不可空;dept_leader / direct_supervisor / initiator_select 勾选即可 |
| 取消勾选 | UI 可不清空已填 userIds / oaRoleIds;发布只校验当前勾选的策略 |
| 发起人自选并存 | 仍按节点 ➕ 选人;自选列表可空;与固定策略结果并集 |
ApproverConfig)export interface ApproverConfig {
/** 审批人:单选(必填)。抄送人:兼容旧数据 / 可选双写首项 */
assigneeType?: AssigneeType
/** 抄送人:多选选人方式(优先)。审批人:不使用 */
assigneeTypes?: AssigneeType[]
userIds?: string[]
oaRoleIds?: string[]
oaRoleCodes?: string[]
signMode?: 'or' | 'and' // 仅审批有意义;抄送引擎侧仍固定 cc
}
assigneeTypes(去重、仅含合法枚举、保持勾选顺序)。assigneeType = assigneeTypes[0],便于未改完的只读展示兜底。userIds / oaRoleIds 始终可随表单保留;解析时仅当对应类型在 assigneeTypes 中才使用。对节点 approverConfig:
assigneeTypes 为非空数组 → 过滤非法项后作为类型列表(抄送多选)。assigneeType → [assigneeType]。[](发布校验会拦;运行时抄送视为 0 人)。审批节点:忽略 assigneeTypes(即使误写入),只认 assigneeType。
oa_assignee.py保留现有 resolve_assignees(单策略、失败抛错)供审批使用。
新增(名称可微调,语义如下):
resolve_cc_assignees(...) -> (user_ids: List[str], meta: Dict)
行为:
types。t in types:构造单策略临时 config,调用现有单策略逻辑(或内联相同分支)。try/except AssigneeResolveError:捕获则跳过该策略,并在 meta.skipped 记录 { type, reason }。meta.resolve_source 建议:
"multi",并带 sources: [...]([], meta) 合法。initiator_select:仍使用调用方传入的 initiator_select_user_ids;空则该策略跳过。
oa_engine.py · _create_tasks_for_node(及预览同类路径)对 ntype == "cc":
resolve_cc_assignees(不再先特判「仅 initiator_select 且空则 return」——该语义被多策略软跳过覆盖)。assignees 为空 → return created(不抛错)。task_type=cc、status=done 任务。对 ntype == "approval":行为不变(resolve_assignees + 硬失败;自选空仍抛错)。
预览 / 进度未达节点解析抄送人员:同样走 resolve_cc_assignees,空列表展示为无人抄送(不阻断预览)。
判断节点是否可 ➕ 自选:
assigneeType === 'initiator_select'initiator_select固定策略解析出的人只读展示;自选人仍写入按节点 map,提交后与固定人并集由引擎完成。
oa_publish.py)对 type === "cc":
| 条件 | 结果 |
|---|---|
| 类型列表为空 | 错误:未配置抄送人策略 |
| 含非法类型 | 错误 |
含 users 且 userIds 为空 |
错误:指定成员不能为空 |
含 oa_roles 且角色 id/code 皆空 |
错误:工作流角色不能为空 |
| 仅含 dept_leader / direct_supervisor / initiator_select | 通过 |
对 type === "approval":保持现有单 assigneeType 校验,不启用 assigneeTypes。
users 时展示指定成员区;勾选 oa_roles 时展示角色区;可同时展开。指定成员 + 部门负责人;过长可截断。| 场景 | 行为 |
|---|---|
旧抄送仅有 assigneeType |
读作单元素列表;运行/发布与现网一致 |
新抄送有 assigneeTypes |
按多选;双写首项 assigneeType |
| 旧运行中实例 | 已落库任务不受影响;未达节点若重解析则走新逻辑(旧 JSON 无 assigneeTypes 时仍单策略) |
审批节点误带 assigneeTypes |
引擎/发布忽略,只认 assigneeType |
无需 SQL patch。
| 路径 | 变更 |
|---|---|
web-admin/.../flow-tree/types.ts |
assigneeTypes;摘要文案;默认抄送配置 |
web-admin/.../flow-tree/FlowDesigner.vue |
抄送多选 UI;读写;校验提示 |
web-admin/.../OaFlowPreview.vue(及同类) |
includes('initiator_select') |
web-admin/.../OaProgressChain.vue(若展示策略名) |
支持 multi / 多标签 |
backend/app/services/oa_assignee.py |
resolve_cc_assignees |
backend/app/services/oa_publish.py |
抄送多类型校验 |
backend/app/services/oa_engine.py |
抄送建任务/预览走新解析 |
backend/tests/(若有 OA 测例) |
并集、软跳过、0 人、发布校验、旧字段兼容 |
assigneeType=users 抄送,指定成员仍抄送成功。assigneeTypes 含 initiator_select 时节点仍可点选加人。meta.resolve_source === "multi" 时进度文案具体用「多种选人方式」还是拼接中文标签——实现时与现有 OaProgressChain 文案表对齐即可。| 日期 | 说明 |
|---|---|
| 2026-07-30 | 初稿:仅抄送多选;软跳过+允许 0 人;发布至少一种且 users/roles 配置要齐;assigneeTypes + 旧字段兼容 + 双写首项 |