2026-07-23-oa-initiator-select-in-flow-design.md 9.4 KB

发起审批 · 流程节点内「发起人自选」(钉钉式)

日期: 2026-07-23
范围:

  • web-admin /#/system-manage/oa-approval/start 发起弹窗
  • web-admin OaFlowPreview(审批流程预览时间轴)
  • backend 发起 / 流程预览 / 引擎推进 / 进度未达节点解析

关联:

  • docs/superpowers/specs/2026-07-15-oa-start-flow-preview-design.md(首版预览;自选曾用顶部选择器)
  • docs/superpowers/specs/2026-07-17-oa-start-flow-preview-resolved-path-design.md(条件未齐占位 / 齐后最终路径)
  • docs/superpowers/specs/2026-07-14-oa-approval-dingtalk-design.md §5.3(initiator_select 解析)

关系: 本规格取代上述两份预览规格中「自选仍用弹窗顶部选择器 / 节点内点选为非目标」的约定;条件门闩、最终路径、卡片视觉仍沿用 2026-07-17。


1. 背景与目标

现状

  • 流程设计中,审批人与抄送人节点均可配置 assigneeType = initiator_select(发起人自选)。
  • 发起弹窗在表单上方放一块全局「自选审批人」多选,整单共用扁平 initiatorSelectUserIds。
  • 底部「审批流程」为只读预览;自选节点未选人时 people 为空。
  • 钉钉:自选嵌在审批流程时间轴对应节点,右侧 ➕ 弹窗加人;审批 / 抄送各自独立。
  • 引擎缺口:同意后 advance_from 不传自选人列表;若后续节点仍是 initiator_select,无法正确建任务。进度「未到达」自选节点也只能从已建任务反推,无法覆盖尚未到达的节点。

目标

  1. 去掉发起弹窗顶部「自选审批人」表单项。
  2. 在「审批流程」时间轴内,对每个 initiator_select 的审批 / 抄送节点按钉钉方式用 ➕ 按节点选人。
  3. 入参改为按节点映射;实例持久化映射,供后续推进与进度预览使用。
  4. 校验:自选审批节点至少 1 人;自选抄送节点可为 0(不建抄送任务)。

非目标

  • 改流程设计器(选人策略枚举已支持,无需改)。
  • 详情页进度轴改为可编辑选人。
  • 兼容旧扁平字段 initiatorSelectUserIds(本期从 Body/类型中删除;多余字段由 Pydantic 忽略,不做双读双写)。
  • 同实例 resubmit_instance(已废弃)。

2. 产品约定(已确认)

项 结论
选人粒度 按节点各自选人(对齐钉钉)
UI 位置 「审批流程」时间轴节点内;去掉顶部全局选择器
自选审批节点 必选 ≥1 人,否则不可提交
自选抄送节点 可不选;空则不生成抄送任务
未选占位文案 审批:「请选择审批人」;抄送:「请选择抄送人」
多人 节点内可多选;chip 可删;右侧保留 ➕ 继续加
与条件预览 路径未确定(resolved=false)时不展示可点选节点;路径确定后再选
自选是否挡整段预览 否(沿用 2026-07-17:路径齐即可出链,自选人可后补)

3. 交互(发起弹窗)

3.1 布局顺序

  1. 动态表单(form-create)
  2. 「审批流程」预览(可交互选人)
  3. 底栏:取消 / 发起

不再出现顶部「自选审批人」el-form-item。

3.2 自选节点卡片(resolved=true 且 assigneeType=initiator_select)

区域 行为
左侧 与现有一致:节点名、签批/抄送徽章、meta(可为「发起人自选」)
中间人员区 已选:人员 chip(头字 + 姓名);未选:灰色占位文案
右侧 方形 ➕ 按钮;点击打开选人弹窗(租户内启用用户,可多选、可搜索)
删除 chip 上可移除该人;移除后若该节点映射为空则恢复占位文案

非 initiator_select 节点保持只读(现有解析出的 chip,无 ➕)。

自选节点的 chip / 占位以前端本地 initiatorSelectByNode 为展示源(结合已加载的用户昵称);flow-preview 仍传入 map,主要用于非自选节点解析及与后端预览一致,不阻塞本地 chip 即时更新。

3.3 提交校验(前端)

对当前预览 steps 中每个 type=approval 且 assigneeType=initiator_select 的节点:若映射中该 nodeId 人数为 0,拦截并提示(如「请为「{节点名}」选择审批人」)。

抄送自选不校验人数。

表单校验、条件路径未确定时的提交失败逻辑保持现有行为。

3.4 预览刷新

  • 表单变化:沿用防抖刷新 flow-preview。
  • 某节点自选人变化:更新本地映射并刷新预览(短防抖或立即),使该步 people 与 chip 一致。
  • 切换模板 / 关闭弹窗:清空映射。

4. 数据与 API

4.1 请求体字段

新增(camelCase 对外):

initiatorSelectByNode?: Record<string, string[]>
// key = 流程节点 id(flow tree node.id)
// value = 用户 id 列表(去重、保序)

涉及:

接口 变更
POST .../templates/{appId}/flow-preview Body 用 initiatorSelectByNode 替换 initiatorSelectUserIds
POST .../instances(发起) 同上
OaResubmitBody(若仍暴露) 同上,仅保持 schema 一致;运行时仍废弃

废弃:initiatorSelectUserIds: string[]。

4.2 预览响应

steps[] 形状不变;对自选节点:

  • 有人选:people 为解析后的 { id, nickName }[]
  • 无人选:people: [],meta 可为「发起人自选」,resolveError 为空(不因未选报错)
  • 前端靠 assigneeType === 'initiator_select' 决定是否渲染 ➕ / 占位(后端须稳定返回该字段,现已有)

4.3 持久化

oa_instance 新增列:

列 类型 说明
initiator_select_json TEXT / NULL JSON 对象:{ "<nodeId>": ["userId", ...] }
  • 发起成功时原样写入提交体中的 initiatorSelectByNode(JSON);推进时按当前节点 id 取值。
  • docs/sql/ 增加 patch;模型 OaInstance 同步字段。
  • 详情可选带回该字段;本期发起成功后关弹窗即可。进度未达节点人员解析从该列读取。

4.4 引擎解析

将原「整单一份 initiator_select_user_ids」改为「按当前节点 id 从 map 取 list」:

节点类型 map 中为空或缺失
approval + initiator_select 抛错(可读文案:发起人未选择审批人 / 指定节点)
cc + initiator_select 不抛错;不创建抄送任务,继续向后推进
其他 assigneeType 行为不变

调用链需贯穿:

  • start_instance → advance_from → _create_tasks_for_node → resolve_assignees
  • 任务同意后的 advance_from:从实例 initiator_select_json 读取 map,不再依赖请求入参
  • preview_start_flow / _walk_preview_nodes
  • build_progress_items 未达节点人员:优先实例持久化 map;再回退已有 resolve_source=initiator_select 任务(兼容旧数据)

resolve_assignees 仍接收「当前节点的 userId 列表」;由 advance_from / _create_tasks_for_node 按 nodeId 从 map 取出后再传入。抄送且列表为空时不调用 resolve_assignees,直接跳过建任务。


5. 前端主要改动点

文件 改动
web-admin/src/views/oa-approval/start/index.vue 删除顶部自选表单;维护 initiatorSelectByNode;提交/预览传 map;校验审批自选
web-admin/src/components/oa-approval/OaFlowPreview.vue 自选节点:占位文案、chip、➕、emit 选人/删人;非自选只读
web-admin/src/api/oaApproval.ts 类型与请求字段替换
选人弹窗 复用现有用户列表加载(发起页已有 loadUsers / fetchUserList),可用 el-dialog + el-select multiple 或独立小组件

样式:Fresh Mint;➕ 按钮对齐钉钉位置(卡片行右侧),无 backdrop blur;不引入小程序色值。


6. 后端主要改动点

文件 改动
docs/sql/patch_2026-07-23_oa_instance_initiator_select_json.sql 加列
backend/app/models/models.py OaInstance.initiator_select_json
backend/app/api/oa_approval.py Body 字段替换
backend/app/services/oa_engine.py map 贯穿 start / advance / preview / progress;CC 空跳过
backend/app/services/oa_assignee.py 按传入 list 解析;空 list 时由调用方决定是否调用(审批调用前校验,抄送跳过)
backend/tests/ 按节点 map、审批必填、抄送可空、持久化后二次推进

7. 验收标准

  1. 仅审批节点为自选:发起弹窗无顶部选择器;流程卡片可 ➕ 选人;不选无法发起;选后可提交且待办落在所选人。
  2. 仅抄送节点为自选:可不选人即发起;选人则「抄送我的」可见对应人。
  3. 同一流程多个自选节点(审批 + 抄送,或两级自选审批):人选互不影响;各自校验规则生效。
  4. 条件分支:未齐时仍占位;齐后仅最终路径上的自选节点可点选。
  5. 首节点非自选、后续为自选审批:第一级同意后,第二级能正确用持久化 map 建任务(修复既有缺口)。
  6. 无自选节点的流程:行为与现网一致,无多余 ➕。

8. 修订记录

日期 说明
2026-07-23 初稿:钉钉式节点内自选;按节点 map;审批必填 / 抄送可空;实例持久化